From 9cde96993e9f996b44f417570ba05455472efa08 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 20 Apr 2023 11:31:44 +0200 Subject: [PATCH 01/70] Reworked the ClipperLib / Polygon types to use the tbb::scallable_allocator to better scale on multiple threads. --- CMakeLists.txt | 1 + src/clipper/CMakeLists.txt | 2 + src/clipper/clipper.cpp | 10 +- src/clipper/clipper.hpp | 40 +++-- src/libnest2d/CMakeLists.txt | 2 +- .../Arachne/SkeletalTrapezoidation.cpp | 8 +- .../Arachne/SkeletalTrapezoidation.hpp | 2 +- src/libslic3r/Arachne/utils/VoronoiUtils.cpp | 4 +- src/libslic3r/Arachne/utils/VoronoiUtils.hpp | 2 +- src/libslic3r/BoundingBox.cpp | 147 ++++++++---------- src/libslic3r/BoundingBox.hpp | 125 +++++++-------- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/ClipperUtils.cpp | 12 +- src/libslic3r/ClipperZUtils.hpp | 8 +- src/libslic3r/EdgeGrid.hpp | 2 +- src/libslic3r/ExPolygon.cpp | 4 +- src/libslic3r/GCode/ExtrusionProcessor.hpp | 6 +- src/libslic3r/GCode/SeamPlacer.cpp | 2 +- src/libslic3r/Geometry.hpp | 5 - src/libslic3r/JumpPointSearch.cpp | 6 +- src/libslic3r/MultiPoint.cpp | 4 +- src/libslic3r/MultiPoint.hpp | 2 +- src/libslic3r/Point.cpp | 8 +- src/libslic3r/Point.hpp | 23 +-- src/libslic3r/Polygon.cpp | 2 +- src/libslic3r/Polygon.hpp | 5 +- src/libslic3r/PolygonTrimmer.hpp | 2 +- src/libslic3r/Polyline.hpp | 5 +- src/libslic3r/Print.cpp | 4 +- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintObject.cpp | 4 +- src/libslic3r/SLA/ConcaveHull.cpp | 3 +- src/libslic3r/ShortestPath.hpp | 9 +- src/libslic3r/libslic3r.h | 8 +- tests/libnest2d/CMakeLists.txt | 2 +- tests/libslic3r/test_geometry.cpp | 26 ++-- tests/sla_print/CMakeLists.txt | 2 +- tests/slic3rutils/CMakeLists.txt | 2 +- 38 files changed, 261 insertions(+), 241 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e19d3147bf..66b9a777b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,6 +406,7 @@ endif() set(TBB_DEBUG 1) find_package(TBB REQUIRED) slic3r_remap_configs(TBB::tbb RelWithDebInfo Release) +slic3r_remap_configs(TBB::tbbmalloc RelWithDebInfo Release) # include_directories(${TBB_INCLUDE_DIRS}) # add_definitions(${TBB_DEFINITIONS}) # if(MSVC) diff --git a/src/clipper/CMakeLists.txt b/src/clipper/CMakeLists.txt index 3cb7cb6bbd..1c1cfd5a7c 100644 --- a/src/clipper/CMakeLists.txt +++ b/src/clipper/CMakeLists.txt @@ -8,3 +8,5 @@ add_library(clipper STATIC clipper_z.cpp clipper_z.hpp ) + +target_link_libraries(clipper TBB::tbb TBB::tbbmalloc) diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index c4b7e8bc29..c775a3226c 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -784,7 +784,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) return false; // Allocate a new edge array. - std::vector edges(highI + 1); + Edges edges(highI + 1); // Fill in the edge array. bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data()); if (result) @@ -1079,7 +1079,7 @@ Clipper::Clipper(int initOptions) : void Clipper::Reset() { ClipperBase::Reset(); - m_Scanbeam = std::priority_queue(); + m_Scanbeam = std::priority_queue{}; m_Maxima.clear(); m_ActiveEdges = 0; m_SortedEdges = 0; @@ -2226,8 +2226,8 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) if (!eLastHorz->NextInLML) eMaxPair = GetMaximaPair(eLastHorz); - std::vector::const_iterator maxIt; - std::vector::const_reverse_iterator maxRit; + cInts::const_iterator maxIt; + cInts::const_reverse_iterator maxRit; if (!m_Maxima.empty()) { //get the first maxima in range (X) ... @@ -3941,7 +3941,7 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) return; } - std::vector outPts(size); + OutPts outPts(size); for (size_t i = 0; i < size; ++i) { outPts[i].Pt = in_poly[i]; diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index 849672a8f9..a2a6712b25 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -39,6 +39,8 @@ #include +#include + #define CLIPPER_VERSION "6.2.6" //CLIPPERLIB_USE_XYZ: adds a Z member to IntPoint. Adds a minor cost to perfomance. @@ -112,8 +114,10 @@ using DoublePoint = Eigen::Matrix; //------------------------------------------------------------------------------ -typedef std::vector Path; -typedef std::vector Paths; +template +using Allocator = tbb::scalable_allocator; +using Path = std::vector>; +using Paths = std::vector>; inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} @@ -133,7 +137,7 @@ enum JoinType {jtSquare, jtRound, jtMiter}; enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; class PolyNode; -typedef std::vector< PolyNode* > PolyNodes; +typedef std::vector> PolyNodes; class PolyNode { @@ -186,7 +190,7 @@ public: private: PolyTree(const PolyTree &src) = delete; PolyTree& operator=(const PolyTree &src) = delete; - std::vector AllNodes; + std::vector> AllNodes; friend class Clipper; //to access AllNodes }; @@ -277,6 +281,8 @@ enum EdgeSide { esLeft = 1, esRight = 2}; OutPt *Prev; }; + using OutPts = std::vector>; + struct OutRec; struct Join { Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : @@ -312,7 +318,7 @@ public: if (num_paths == 1) return AddPath(*paths_provider.begin(), PolyTyp, Closed); - std::vector num_edges(num_paths, 0); + std::vector> num_edges(num_paths, 0); int num_edges_total = 0; size_t i = 0; for (const Path &pg : paths_provider) { @@ -333,7 +339,7 @@ public: return false; // Allocate a new edge array. - std::vector edges(num_edges_total); + std::vector> edges(num_edges_total); // Fill in the edge array. bool result = false; TEdge *p_edge = edges.data(); @@ -369,7 +375,7 @@ protected: void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); // Local minima (Y, left edge, right edge) sorted by ascending Y. - std::vector m_MinimaList; + std::vector> m_MinimaList; #ifdef CLIPPERLIB_INT32 static constexpr const bool m_UseFullRange = false; @@ -380,7 +386,8 @@ protected: #endif // CLIPPERLIB_INT32 // A vector of edges per each input path. - std::vector> m_edges; + using Edges = std::vector>; + std::vector> m_edges; // Don't remove intermediate vertices of a collinear sequence of points. bool m_PreserveCollinear; // Is any of the paths inserted by AddPath() or AddPaths() open? @@ -424,22 +431,23 @@ protected: private: // Output polygons. - std::vector m_PolyOuts; + std::vector> m_PolyOuts; // Output points, allocated by a continuous sets of m_OutPtsChunkSize. - std::vector m_OutPts; + std::vector> m_OutPts; // List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk. OutPt *m_OutPtsFree; size_t m_OutPtsChunkSize; size_t m_OutPtsChunkLast; - std::vector m_Joins; - std::vector m_GhostJoins; - std::vector m_IntersectList; + std::vector> m_Joins; + std::vector> m_GhostJoins; + std::vector> m_IntersectList; ClipType m_ClipType; // A priority queue (a binary heap) of Y coordinates. - std::priority_queue m_Scanbeam; + using cInts = std::vector>; + std::priority_queue m_Scanbeam; // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). - std::vector m_Maxima; + cInts m_Maxima; TEdge *m_ActiveEdges; TEdge *m_SortedEdges; PolyFillType m_ClipFillType; @@ -530,7 +538,7 @@ private: Paths m_destPolys; Path m_srcPoly; Path m_destPoly; - std::vector m_normals; + std::vector> m_normals; double m_delta, m_sinA, m_sin, m_cos; double m_miterLim, m_StepsPerRad; // x: index of the lowest contour in m_polyNodes diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt index c18dc31cb4..154c965e5b 100644 --- a/src/libnest2d/CMakeLists.txt +++ b/src/libnest2d/CMakeLists.txt @@ -24,5 +24,5 @@ set(LIBNEST2D_SRCFILES add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES}) target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) -target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb Boost::boost libslic3r) +target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb TBB::tbbmalloc Boost::boost libslic3r) target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r) diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index 381f18b700..a73a4918a6 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -181,7 +181,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type& } else { - std::vector discretized = discretize(vd_edge, segments); + Points discretized = discretize(vd_edge, segments); assert(discretized.size() >= 2); if(discretized.size() < 2) { @@ -236,7 +236,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type& } } -std::vector SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_edge, const std::vector& segments) +Points SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_edge, const std::vector& segments) { /*Terminology in this function assumes that the edge moves horizontally from left to right. This is not necessarily the case; the edge can go in any @@ -257,7 +257,7 @@ std::vector SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_ bool point_right = right_cell->contains_point(); if ((!point_left && !point_right) || vd_edge.is_secondary()) // Source vert is directly connected to source segment { - return std::vector({ start, end }); + return Points({ start, end }); } else if (point_left != point_right) //This is a parabolic edge between a point and a line. { @@ -311,7 +311,7 @@ std::vector SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_ //Start generating points along the edge. Point a = start; Point b = end; - std::vector ret; + Points ret; ret.emplace_back(a); //Introduce an extra edge at the borders of the markings? diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp index b4029d586d..7b8ecf8346 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp @@ -204,7 +204,7 @@ protected: * \return A number of coordinates along the edge where the edge is broken * up into discrete pieces. */ - std::vector discretize(const vd_t::edge_type& segment, const std::vector& segments); + Points discretize(const vd_t::edge_type& segment, const std::vector& segments); /*! * Compute the range of line segments that surround a cell of the skeletal diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp index 069e1f5ad5..675a0ebb42 100644 --- a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp +++ b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp @@ -138,9 +138,9 @@ public: return Point(coord_t(p.x() * matrix[0] + p.y() * matrix[2]), coord_t(p.x() * matrix[1] + p.y() * matrix[3])); } }; -std::vector VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle) +Points VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle) { - std::vector discretized; + Points discretized; // x is distance of point projected on the segment ab // xx is point projected on the segment ab const Point a = segment.from(); diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp index aa46936437..ea6a8495a1 100644 --- a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp +++ b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp @@ -34,7 +34,7 @@ public: * Discretize a parabola based on (approximate) step size. * The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola. */ - static std::vector discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle); + static Points discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle); static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex) { diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp index 4f52c5108d..10543fc955 100644 --- a/src/libslic3r/BoundingBox.cpp +++ b/src/libslic3r/BoundingBox.cpp @@ -6,23 +6,19 @@ namespace Slic3r { -template BoundingBoxBase::BoundingBoxBase(const std::vector &points); +template BoundingBoxBase::BoundingBoxBase(const Points &points); template BoundingBoxBase::BoundingBoxBase(const std::vector &points); template BoundingBox3Base::BoundingBox3Base(const std::vector &points); void BoundingBox::polygon(Polygon* polygon) const { - polygon->points.clear(); - polygon->points.resize(4); - polygon->points[0](0) = this->min(0); - polygon->points[0](1) = this->min(1); - polygon->points[1](0) = this->max(0); - polygon->points[1](1) = this->min(1); - polygon->points[2](0) = this->max(0); - polygon->points[2](1) = this->max(1); - polygon->points[3](0) = this->min(0); - polygon->points[3](1) = this->max(1); + polygon->points = { + this->min, + { this->max.x(), this->min.y() }, + this->max, + { this->min.x(), this->max.y() } + }; } Polygon BoundingBox::polygon() const @@ -37,8 +33,8 @@ BoundingBox BoundingBox::rotated(double angle) const BoundingBox out; out.merge(this->min.rotated(angle)); out.merge(this->max.rotated(angle)); - out.merge(Point(this->min(0), this->max(1)).rotated(angle)); - out.merge(Point(this->max(0), this->min(1)).rotated(angle)); + out.merge(Point(this->min.x(), this->max.y()).rotated(angle)); + out.merge(Point(this->max.x(), this->min.y()).rotated(angle)); return out; } @@ -47,23 +43,23 @@ BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const BoundingBox out; out.merge(this->min.rotated(angle, center)); out.merge(this->max.rotated(angle, center)); - out.merge(Point(this->min(0), this->max(1)).rotated(angle, center)); - out.merge(Point(this->max(0), this->min(1)).rotated(angle, center)); + out.merge(Point(this->min.x(), this->max.y()).rotated(angle, center)); + out.merge(Point(this->max.x(), this->min.y()).rotated(angle, center)); return out; } -template void -BoundingBoxBase::scale(double factor) +template void +BoundingBoxBase::scale(double factor) { this->min *= factor; this->max *= factor; } -template void BoundingBoxBase::scale(double factor); +template void BoundingBoxBase::scale(double factor); template void BoundingBoxBase::scale(double factor); template void BoundingBoxBase::scale(double factor); -template void -BoundingBoxBase::merge(const PointClass &point) +template void +BoundingBoxBase::merge(const PointType &point) { if (this->defined) { this->min = this->min.cwiseMin(point); @@ -74,22 +70,22 @@ BoundingBoxBase::merge(const PointClass &point) this->defined = true; } } -template void BoundingBoxBase::merge(const Point &point); +template void BoundingBoxBase::merge(const Point &point); template void BoundingBoxBase::merge(const Vec2f &point); template void BoundingBoxBase::merge(const Vec2d &point); -template void -BoundingBoxBase::merge(const std::vector &points) +template void +BoundingBoxBase::merge(const PointsType &points) { this->merge(BoundingBoxBase(points)); } -template void BoundingBoxBase::merge(const Points &points); +template void BoundingBoxBase::merge(const Points &points); template void BoundingBoxBase::merge(const Pointfs &points); -template void -BoundingBoxBase::merge(const BoundingBoxBase &bb) +template void +BoundingBoxBase::merge(const BoundingBoxBase &bb) { - assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1)); + assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y()); if (bb.defined) { if (this->defined) { this->min = this->min.cwiseMin(bb.min); @@ -101,12 +97,12 @@ BoundingBoxBase::merge(const BoundingBoxBase &bb) } } } -template void BoundingBoxBase::merge(const BoundingBoxBase &bb); +template void BoundingBoxBase::merge(const BoundingBoxBase &bb); template void BoundingBoxBase::merge(const BoundingBoxBase &bb); template void BoundingBoxBase::merge(const BoundingBoxBase &bb); -template void -BoundingBox3Base::merge(const PointClass &point) +template void +BoundingBox3Base::merge(const PointType &point) { if (this->defined) { this->min = this->min.cwiseMin(point); @@ -120,17 +116,17 @@ BoundingBox3Base::merge(const PointClass &point) template void BoundingBox3Base::merge(const Vec3f &point); template void BoundingBox3Base::merge(const Vec3d &point); -template void -BoundingBox3Base::merge(const std::vector &points) +template void +BoundingBox3Base::merge(const PointsType &points) { this->merge(BoundingBox3Base(points)); } template void BoundingBox3Base::merge(const Pointf3s &points); -template void -BoundingBox3Base::merge(const BoundingBox3Base &bb) +template void +BoundingBox3Base::merge(const BoundingBox3Base &bb) { - assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2)); + assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z()); if (bb.defined) { if (this->defined) { this->min = this->min.cwiseMin(bb.min); @@ -144,83 +140,78 @@ BoundingBox3Base::merge(const BoundingBox3Base &bb) } template void BoundingBox3Base::merge(const BoundingBox3Base &bb); -template PointClass -BoundingBoxBase::size() const +template PointType +BoundingBoxBase::size() const { - return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1)); + return this->max - this->min; } -template Point BoundingBoxBase::size() const; +template Point BoundingBoxBase::size() const; template Vec2f BoundingBoxBase::size() const; template Vec2d BoundingBoxBase::size() const; -template PointClass -BoundingBox3Base::size() const +template PointType +BoundingBox3Base::size() const { - return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2)); + return this->max - this->min; } template Vec3f BoundingBox3Base::size() const; template Vec3d BoundingBox3Base::size() const; -template double BoundingBoxBase::radius() const +template double BoundingBoxBase::radius() const { assert(this->defined); - double x = this->max(0) - this->min(0); - double y = this->max(1) - this->min(1); - return 0.5 * sqrt(x*x+y*y); + return 0.5 * (this->max - this->min).cast().norm(); } -template double BoundingBoxBase::radius() const; +template double BoundingBoxBase::radius() const; template double BoundingBoxBase::radius() const; -template double BoundingBox3Base::radius() const +template double BoundingBox3Base::radius() const { - double x = this->max(0) - this->min(0); - double y = this->max(1) - this->min(1); - double z = this->max(2) - this->min(2); - return 0.5 * sqrt(x*x+y*y+z*z); + return 0.5 * (this->max - this->min).cast().norm(); } template double BoundingBox3Base::radius() const; -template void -BoundingBoxBase::offset(coordf_t delta) +template void +BoundingBoxBase::offset(coordf_t delta) { - PointClass v(delta, delta); + PointType v(delta, delta); this->min -= v; this->max += v; } -template void BoundingBoxBase::offset(coordf_t delta); +template void BoundingBoxBase::offset(coordf_t delta); template void BoundingBoxBase::offset(coordf_t delta); -template void -BoundingBox3Base::offset(coordf_t delta) +template void +BoundingBox3Base::offset(coordf_t delta) { - PointClass v(delta, delta, delta); + PointType v(delta, delta, delta); this->min -= v; this->max += v; } template void BoundingBox3Base::offset(coordf_t delta); -template PointClass -BoundingBoxBase::center() const +template PointType +BoundingBoxBase::center() const { return (this->min + this->max) / 2; } -template Point BoundingBoxBase::center() const; +template Point BoundingBoxBase::center() const; template Vec2f BoundingBoxBase::center() const; template Vec2d BoundingBoxBase::center() const; -template PointClass -BoundingBox3Base::center() const +template PointType +BoundingBox3Base::center() const { return (this->min + this->max) / 2; } template Vec3f BoundingBox3Base::center() const; template Vec3d BoundingBox3Base::center() const; -template coordf_t -BoundingBox3Base::max_size() const +template coordf_t +BoundingBox3Base::max_size() const { - PointClass s = size(); - return std::max(s(0), std::max(s(1), s(2))); + PointType s = size(); + return std::max(s.x(), std::max(s.y(), s.z())); } template coordf_t BoundingBox3Base::max_size() const; template coordf_t BoundingBox3Base::max_size() const; @@ -228,8 +219,8 @@ template coordf_t BoundingBox3Base::max_size() const; void BoundingBox::align_to_grid(const coord_t cell_size) { if (this->defined) { - min(0) = Slic3r::align_to_grid(min(0), cell_size); - min(1) = Slic3r::align_to_grid(min(1), cell_size); + min.x() = Slic3r::align_to_grid(min.x(), cell_size); + min.y() = Slic3r::align_to_grid(min.y(), cell_size); } } @@ -238,14 +229,14 @@ BoundingBoxf3 BoundingBoxf3::transformed(const Transform3d& matrix) const typedef Eigen::Matrix Vertices; Vertices src_vertices; - src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2); - src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2); - src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2); - src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2); - src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2); - src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2); - src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2); - src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2); + src_vertices(0, 0) = min.x(); src_vertices(1, 0) = min.y(); src_vertices(2, 0) = min.z(); + src_vertices(0, 1) = max.x(); src_vertices(1, 1) = min.y(); src_vertices(2, 1) = min.z(); + src_vertices(0, 2) = max.x(); src_vertices(1, 2) = max.y(); src_vertices(2, 2) = min.z(); + src_vertices(0, 3) = min.x(); src_vertices(1, 3) = max.y(); src_vertices(2, 3) = min.z(); + src_vertices(0, 4) = min.x(); src_vertices(1, 4) = min.y(); src_vertices(2, 4) = max.z(); + src_vertices(0, 5) = max.x(); src_vertices(1, 5) = min.y(); src_vertices(2, 5) = max.z(); + src_vertices(0, 6) = max.x(); src_vertices(1, 6) = max.y(); src_vertices(2, 6) = max.z(); + src_vertices(0, 7) = min.x(); src_vertices(1, 7) = max.y(); src_vertices(2, 7) = max.z(); Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous(); diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index d741be36cc..fc1b500740 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -8,53 +8,54 @@ namespace Slic3r { -template +template > class BoundingBoxBase { public: - PointClass min; - PointClass max; + using PointsType = APointsType; + PointType min; + PointType max; bool defined; - BoundingBoxBase() : min(PointClass::Zero()), max(PointClass::Zero()), defined(false) {} - BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : + BoundingBoxBase() : min(PointType::Zero()), max(PointType::Zero()), defined(false) {} + BoundingBoxBase(const PointType &pmin, const PointType &pmax) : min(pmin), max(pmax), defined(pmin.x() < pmax.x() && pmin.y() < pmax.y()) {} - BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) : + BoundingBoxBase(const PointType &p1, const PointType &p2, const PointType &p3) : min(p1), max(p1), defined(false) { merge(p2); merge(p3); } template> BoundingBoxBase(It from, It to) { construct(*this, from, to); } - BoundingBoxBase(const std::vector &points) + BoundingBoxBase(const PointsType &points) : BoundingBoxBase(points.begin(), points.end()) {} - void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); } - void merge(const PointClass &point); - void merge(const std::vector &points); - void merge(const BoundingBoxBase &bb); + void reset() { this->defined = false; this->min = PointType::Zero(); this->max = PointType::Zero(); } + void merge(const PointType &point); + void merge(const PointsType &points); + void merge(const BoundingBoxBase &bb); void scale(double factor); - PointClass size() const; + PointType size() const; double radius() const; - void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; } - void translate(const PointClass &v) { this->min += v; this->max += v; } + void translate(coordf_t x, coordf_t y) { assert(this->defined); PointType v(x, y); this->min += v; this->max += v; } + void translate(const PointType &v) { this->min += v; this->max += v; } void offset(coordf_t delta); - BoundingBoxBase inflated(coordf_t delta) const throw() { BoundingBoxBase out(*this); out.offset(delta); return out; } - PointClass center() const; - bool contains(const PointClass &point) const { + BoundingBoxBase inflated(coordf_t delta) const throw() { BoundingBoxBase out(*this); out.offset(delta); return out; } + PointType center() const; + bool contains(const PointType &point) const { return point.x() >= this->min.x() && point.x() <= this->max.x() && point.y() >= this->min.y() && point.y() <= this->max.y(); } - bool contains(const BoundingBoxBase &other) const { + bool contains(const BoundingBoxBase &other) const { return contains(other.min) && contains(other.max); } - bool overlap(const BoundingBoxBase &other) const { + bool overlap(const BoundingBoxBase &other) const { return ! (this->max.x() < other.min.x() || this->min.x() > other.max.x() || this->max.y() < other.min.y() || this->min.y() > other.max.y()); } - bool operator==(const BoundingBoxBase &rhs) { return this->min == rhs.min && this->max == rhs.max; } - bool operator!=(const BoundingBoxBase &rhs) { return ! (*this == rhs); } + bool operator==(const BoundingBoxBase &rhs) { return this->min == rhs.min && this->max == rhs.max; } + bool operator!=(const BoundingBoxBase &rhs) { return ! (*this == rhs); } private: // to access construct() @@ -69,10 +70,10 @@ private: { if (from != to) { auto it = from; - out.min = it->template cast(); + out.min = it->template cast(); out.max = out.min; for (++ it; it != to; ++ it) { - auto vec = it->template cast(); + auto vec = it->template cast(); out.min = out.min.cwiseMin(vec); out.max = out.max.cwiseMax(vec); } @@ -81,16 +82,18 @@ private: } }; -template -class BoundingBox3Base : public BoundingBoxBase +template +class BoundingBox3Base : public BoundingBoxBase> { public: - BoundingBox3Base() : BoundingBoxBase() {} - BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : - BoundingBoxBase(pmin, pmax) - { if (pmin.z() >= pmax.z()) BoundingBoxBase::defined = false; } - BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) : - BoundingBoxBase(p1, p1) { merge(p2); merge(p3); } + using PointsType = std::vector; + + BoundingBox3Base() : BoundingBoxBase() {} + BoundingBox3Base(const PointType &pmin, const PointType &pmax) : + BoundingBoxBase(pmin, pmax) + { if (pmin.z() >= pmax.z()) BoundingBoxBase::defined = false; } + BoundingBox3Base(const PointType &p1, const PointType &p2, const PointType &p3) : + BoundingBoxBase(p1, p1) { merge(p2); merge(p3); } template > BoundingBox3Base(It from, It to) { @@ -98,67 +101,67 @@ public: throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor"); auto it = from; - this->min = it->template cast(); + this->min = it->template cast(); this->max = this->min; for (++ it; it != to; ++ it) { - auto vec = it->template cast(); + auto vec = it->template cast(); this->min = this->min.cwiseMin(vec); this->max = this->max.cwiseMax(vec); } this->defined = (this->min.x() < this->max.x()) && (this->min.y() < this->max.y()) && (this->min.z() < this->max.z()); } - BoundingBox3Base(const std::vector &points) + BoundingBox3Base(const PointsType &points) : BoundingBox3Base(points.begin(), points.end()) {} - void merge(const PointClass &point); - void merge(const std::vector &points); - void merge(const BoundingBox3Base &bb); - PointClass size() const; + void merge(const PointType &point); + void merge(const PointsType &points); + void merge(const BoundingBox3Base &bb); + PointType size() const; double radius() const; - void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; } + void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointType v(x, y, z); this->min += v; this->max += v; } void translate(const Vec3d &v) { this->min += v; this->max += v; } void offset(coordf_t delta); - BoundingBox3Base inflated(coordf_t delta) const throw() { BoundingBox3Base out(*this); out.offset(delta); return out; } - PointClass center() const; + BoundingBox3Base inflated(coordf_t delta) const throw() { BoundingBox3Base out(*this); out.offset(delta); return out; } + PointType center() const; coordf_t max_size() const; - bool contains(const PointClass &point) const { - return BoundingBoxBase::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z(); + bool contains(const PointType &point) const { + return BoundingBoxBase::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z(); } - bool contains(const BoundingBox3Base& other) const { + bool contains(const BoundingBox3Base& other) const { return contains(other.min) && contains(other.max); } // Intersects without boundaries. - bool intersects(const BoundingBox3Base& other) const { + bool intersects(const BoundingBox3Base& other) const { return this->min.x() < other.max.x() && this->max.x() > other.min.x() && this->min.y() < other.max.y() && this->max.y() > other.min.y() && this->min.z() < other.max.z() && this->max.z() > other.min.z(); } }; // Will prevent warnings caused by non existing definition of template in hpp -extern template void BoundingBoxBase::scale(double factor); +extern template void BoundingBoxBase::scale(double factor); extern template void BoundingBoxBase::scale(double factor); extern template void BoundingBoxBase::scale(double factor); -extern template void BoundingBoxBase::offset(coordf_t delta); +extern template void BoundingBoxBase::offset(coordf_t delta); extern template void BoundingBoxBase::offset(coordf_t delta); -extern template void BoundingBoxBase::merge(const Point &point); +extern template void BoundingBoxBase::merge(const Point &point); extern template void BoundingBoxBase::merge(const Vec2f &point); extern template void BoundingBoxBase::merge(const Vec2d &point); -extern template void BoundingBoxBase::merge(const Points &points); +extern template void BoundingBoxBase::merge(const Points &points); extern template void BoundingBoxBase::merge(const Pointfs &points); -extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); +extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); -extern template Point BoundingBoxBase::size() const; +extern template Point BoundingBoxBase::size() const; extern template Vec2f BoundingBoxBase::size() const; extern template Vec2d BoundingBoxBase::size() const; -extern template double BoundingBoxBase::radius() const; +extern template double BoundingBoxBase::radius() const; extern template double BoundingBoxBase::radius() const; -extern template Point BoundingBoxBase::center() const; +extern template Point BoundingBoxBase::center() const; extern template Vec2f BoundingBoxBase::center() const; extern template Vec2d BoundingBoxBase::center() const; extern template void BoundingBox3Base::merge(const Vec3f &point); @@ -174,7 +177,7 @@ extern template Vec3d BoundingBox3Base::center() const; extern template coordf_t BoundingBox3Base::max_size() const; extern template coordf_t BoundingBox3Base::max_size() const; -class BoundingBox : public BoundingBoxBase +class BoundingBox : public BoundingBoxBase { public: void polygon(Polygon* polygon) const; @@ -187,9 +190,9 @@ public: // to encompass the original bounding box. void align_to_grid(const coord_t cell_size); - BoundingBox() : BoundingBoxBase() {} - BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {} - BoundingBox(const Points &points) : BoundingBoxBase(points) {} + BoundingBox() : BoundingBoxBase() {} + BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {} + BoundingBox(const Points &points) : BoundingBoxBase(points) {} BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; } @@ -222,14 +225,14 @@ public: BoundingBoxf3 transformed(const Transform3d& matrix) const; }; -template -inline bool empty(const BoundingBoxBase &bb) +template +inline bool empty(const BoundingBoxBase &bb) { return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y(); } -template -inline bool empty(const BoundingBox3Base &bb) +template +inline bool empty(const BoundingBox3Base &bb) { return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z(); } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 61046e961a..53074d3bd8 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -485,6 +485,7 @@ target_link_libraries(libslic3r qhull semver TBB::tbb + TBB::tbbmalloc libslic3r_cgal ${CMAKE_DL_LIBS} PNG::PNG diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 2869d0c870..d83fe4e48e 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -51,9 +51,11 @@ namespace ClipperUtils { // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. // Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one // with a set of polygons covering the whole layer below. - template - inline void clip_clipper_polygon_with_subject_bbox_templ(const std::vector &src, const BoundingBox &bbox, std::vector &out) + template + inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox, PointsType &out) { + using PointType = typename PointsType::value_type; + out.clear(); const size_t cnt = src.size(); if (cnt < 3) @@ -108,10 +110,10 @@ namespace ClipperUtils { void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); } - template - [[nodiscard]] std::vector clip_clipper_polygon_with_subject_bbox_templ(const std::vector &src, const BoundingBox &bbox) + template + [[nodiscard]] PointsType clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox) { - std::vector out; + PointsType out; clip_clipper_polygon_with_subject_bbox(src, bbox, out); return out; } diff --git a/src/libslic3r/ClipperZUtils.hpp b/src/libslic3r/ClipperZUtils.hpp index 4ae78ae235..001a3f2daa 100644 --- a/src/libslic3r/ClipperZUtils.hpp +++ b/src/libslic3r/ClipperZUtils.hpp @@ -40,7 +40,7 @@ inline ZPath to_zpath(const Points &path, coord_t z) // Convert multiple paths to paths with a given Z coordinate. // If Open, then duplicate the first point of each path at its end. template -inline ZPaths to_zpaths(const std::vector &paths, coord_t z) +inline ZPaths to_zpaths(const VecOfPoints &paths, coord_t z) { ZPaths out; out.reserve(paths.size()); @@ -86,16 +86,16 @@ inline Points from_zpath(const ZPoints &path) // Convert multiple paths to paths with a given Z coordinate. // If Open, then duplicate the first point of each path at its end. template -inline void from_zpaths(const ZPaths &paths, std::vector &out) +inline void from_zpaths(const ZPaths &paths, VecOfPoints &out) { out.reserve(out.size() + paths.size()); for (const ZPoints &path : paths) out.emplace_back(from_zpath(path)); } template -inline std::vector from_zpaths(const ZPaths &paths) +inline VecOfPoints from_zpaths(const ZPaths &paths) { - std::vector out; + VecOfPoints out; from_zpaths(paths, out); return out; } diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp index 4be2bdd07c..744a23e186 100644 --- a/src/libslic3r/EdgeGrid.hpp +++ b/src/libslic3r/EdgeGrid.hpp @@ -17,7 +17,7 @@ public: Contour() = default; Contour(const Slic3r::Point *begin, const Slic3r::Point *end, bool open) : m_begin(begin), m_end(end), m_open(open) {} Contour(const Slic3r::Point *data, size_t size, bool open) : Contour(data, data + size, open) {} - Contour(const std::vector &pts, bool open) : Contour(pts.data(), pts.size(), open) {} + Contour(const Points &pts, bool open) : Contour(pts.data(), pts.size(), open) {} const Slic3r::Point *begin() const { return m_begin; } const Slic3r::Point *end() const { return m_end; } diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index b6d12a6023..42f026e0b2 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -397,7 +397,7 @@ bool has_duplicate_points(const ExPolygon &expoly) size_t cnt = expoly.contour.points.size(); for (const Polygon &hole : expoly.holes) cnt += hole.points.size(); - std::vector allpts; + Points allpts; allpts.reserve(cnt); allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end()); for (const Polygon &hole : expoly.holes) @@ -420,7 +420,7 @@ bool has_duplicate_points(const ExPolygons &expolys) // Check globally. #if 0 // Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster. - std::vector allpts; + Points allpts; allpts.reserve(count_points(expolys)); for (const ExPolygon &expoly : expolys) { allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end()); diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 625ea695ce..1488a78a6d 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -110,12 +110,14 @@ struct ExtendedPoint float curvature; }; -template -std::vector estimate_points_properties(const std::vector

&input_points, +template +std::vector estimate_points_properties(const POINTS &input_points, const AABBTreeLines::LinesDistancer &unscaled_prev_layer, float flow_width, float max_line_length = -1.0f) { + using P = typename POINTS::value_type; + using AABBScalar = typename AABBTreeLines::LinesDistancer::Scalar; if (input_points.empty()) return {}; diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 244a103ca8..3bf1edf39e 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -437,7 +437,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, std::vector; - } - namespace Geometry { // Generic result of an orientation predicate. diff --git a/src/libslic3r/JumpPointSearch.cpp b/src/libslic3r/JumpPointSearch.cpp index ef3dba45e7..58a656fa04 100644 --- a/src/libslic3r/JumpPointSearch.cpp +++ b/src/libslic3r/JumpPointSearch.cpp @@ -19,6 +19,8 @@ #include #include +#include + //#define DEBUG_FILES #ifdef DEBUG_FILES #include "libslic3r/SVG.hpp" @@ -267,7 +269,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1) using QNode = astar::QNode>; std::unordered_map astar_cache{}; - std::vector out_path; + std::vector> out_path; std::vector out_nodes; if (!astar::search_route(tracer, {start, {0, 0}}, std::back_inserter(out_nodes), astar_cache)) { @@ -306,7 +308,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1) svg.draw(scaled_point(start), "green", scale_(0.4)); #endif - std::vector tmp_path; + std::vector> tmp_path; tmp_path.reserve(out_path.size()); // Some path found, reverse and remove points that do not change direction std::reverse(out_path.begin(), out_path.end()); diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index f18720bd6f..bb4d62cc0b 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -103,9 +103,9 @@ bool MultiPoint::remove_duplicate_points() return false; } -std::vector MultiPoint::_douglas_peucker(const std::vector& pts, const double tolerance) +Points MultiPoint::_douglas_peucker(const Points &pts, const double tolerance) { - std::vector result_pts; + Points result_pts; double tolerance_sq = tolerance * tolerance; if (! pts.empty()) { const Point *anchor = &pts.front(); diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 778b30c57d..4cf4b5e140 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -110,7 +110,7 @@ public: }; extern BoundingBox get_extents(const MultiPoint &mp); -extern BoundingBox get_extents_rotated(const std::vector &points, double angle); +extern BoundingBox get_extents_rotated(const Points &points, double angle); extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle); inline double length(const Points &pts) { diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp index 09afcc3993..457bb44cef 100644 --- a/src/libslic3r/Point.cpp +++ b/src/libslic3r/Point.cpp @@ -57,7 +57,7 @@ void Point::rotate(double angle, const Point ¢er) (*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx ); } -bool has_duplicate_points(std::vector &&pts) +bool has_duplicate_points(Points &&pts) { std::sort(pts.begin(), pts.end()); for (size_t i = 1; i < pts.size(); ++ i) @@ -97,15 +97,15 @@ template BoundingBox get_extents(const Points &pts); // if IncludeBoundary, then a bounding box is defined even for a single point. // otherwise a bounding box is only defined if it has a positive area. template -BoundingBox get_extents(const std::vector &pts) +BoundingBox get_extents(const VecOfPoints &pts) { BoundingBox bbox; for (const Points &p : pts) bbox.merge(get_extents(p)); return bbox; } -template BoundingBox get_extents(const std::vector &pts); -template BoundingBox get_extents(const std::vector &pts); +template BoundingBox get_extents(const VecOfPoints &pts); +template BoundingBox get_extents(const VecOfPoints &pts); BoundingBoxf get_extents(const std::vector &pts) { diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index d53352f28e..72c739395d 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -9,6 +9,9 @@ #include #include +#include + + #include #include "LocalesUtils.hpp" @@ -49,7 +52,7 @@ using Vec2d = Eigen::Matrix; using Vec3d = Eigen::Matrix; using Vec4d = Eigen::Matrix; -using Points = std::vector; +using Points = std::vector>; using PointPtrs = std::vector; using PointConstPtrs = std::vector; using Points3 = std::vector; @@ -57,6 +60,8 @@ using Pointfs = std::vector; using Vec2ds = std::vector; using Pointf3s = std::vector; +using VecOfPoints = std::vector>; + using Matrix2f = Eigen::Matrix; using Matrix2d = Eigen::Matrix; using Matrix3f = Eigen::Matrix; @@ -247,9 +252,9 @@ extern template BoundingBox get_extents(const Points &pts); // if IncludeBoundary, then a bounding box is defined even for a single point. // otherwise a bounding box is only defined if it has a positive area. template -BoundingBox get_extents(const std::vector &pts); -extern template BoundingBox get_extents(const std::vector &pts); -extern template BoundingBox get_extents(const std::vector &pts); +BoundingBox get_extents(const VecOfPoints &pts); +extern template BoundingBox get_extents(const VecOfPoints &pts); +extern template BoundingBox get_extents(const VecOfPoints &pts); BoundingBoxf get_extents(const std::vector &pts); @@ -263,16 +268,16 @@ inline std::pair nearest_point(const Points &points, const Point &p // Test for duplicate points in a vector of points. // The points are copied, sorted and checked for duplicates globally. -bool has_duplicate_points(std::vector &&pts); -inline bool has_duplicate_points(const std::vector &pts) +bool has_duplicate_points(Points &&pts); +inline bool has_duplicate_points(const Points &pts) { - std::vector cpy = pts; + Points cpy = pts; return has_duplicate_points(std::move(cpy)); } // Test for duplicate points in a vector of points. // Only successive points are checked for equality. -inline bool has_duplicate_successive_points(const std::vector &pts) +inline bool has_duplicate_successive_points(const Points &pts) { for (size_t i = 1; i < pts.size(); ++ i) if (pts[i - 1] == pts[i]) @@ -282,7 +287,7 @@ inline bool has_duplicate_successive_points(const std::vector &pts) // Test for duplicate points in a vector of points. // Only successive points are checked for equality. Additionally, first and last points are compared for equality. -inline bool has_duplicate_successive_points_closed(const std::vector &pts) +inline bool has_duplicate_successive_points_closed(const Points &pts) { return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back()); } diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 3be36984c5..299e22adcd 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -404,7 +404,7 @@ bool has_duplicate_points(const Polygons &polys) // Check globally. #if 0 // Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster. - std::vector allpts; + Points allpts; allpts.reserve(count_points(polys)); for (const Polygon &poly : polys) allpts.insert(allpts.end(), poly.points.begin(), poly.points.end()); diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 3c4bb0e2ad..24a2d69f3a 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -5,6 +5,7 @@ #include #include #include "Line.hpp" +#include "Point.hpp" #include "MultiPoint.hpp" #include "Polyline.hpp" @@ -241,7 +242,7 @@ inline Polylines to_polylines(Polygons &&polys) return polylines; } -inline Polygons to_polygons(const std::vector &paths) +inline Polygons to_polygons(const VecOfPoints &paths) { Polygons out; out.reserve(paths.size()); @@ -250,7 +251,7 @@ inline Polygons to_polygons(const std::vector &paths) return out; } -inline Polygons to_polygons(std::vector &&paths) +inline Polygons to_polygons(VecOfPoints &&paths) { Polygons out; out.reserve(paths.size()); diff --git a/src/libslic3r/PolygonTrimmer.hpp b/src/libslic3r/PolygonTrimmer.hpp index eddffbc7fa..93e94e303f 100644 --- a/src/libslic3r/PolygonTrimmer.hpp +++ b/src/libslic3r/PolygonTrimmer.hpp @@ -17,7 +17,7 @@ namespace EdgeGrid { struct TrimmedLoop { - std::vector points; + Points points; // Number of points per segment. Empty if the loop is std::vector segments; diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 1b23388e18..9ed5f2137c 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -158,9 +158,10 @@ inline void polylines_append(Polylines &dst, Polylines &&src) // src_first: the merge point is at src.begin() or src.end()? // The orientation of the resulting polyline is unknown, the output polyline may start // either with src piece or dst piece. -template -inline void polylines_merge(std::vector &dst, bool dst_first, std::vector &&src, bool src_first) +template +inline void polylines_merge(PointsType &dst, bool dst_first, PointsType &&src, bool src_first) { + using PointType = typename PointsType::value_type; if (dst_first) { if (src_first) std::reverse(dst.begin(), dst.end()); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index b68d04b2b9..49d17e5c0b 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1132,9 +1132,9 @@ Polygons Print::first_layer_islands() const return islands; } -std::vector Print::first_layer_wipe_tower_corners() const +Points Print::first_layer_wipe_tower_corners() const { - std::vector pts_scaled; + Points pts_scaled; if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index d222fbfe29..30f6bb41a7 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -626,7 +626,7 @@ private: // Islands of objects and their supports extruded at the 1st layer. Polygons first_layer_islands() const; // Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim. - std::vector first_layer_wipe_tower_corners() const; + Points first_layer_wipe_tower_corners() const; // Returns true if any of the print_objects has print_object_step valid. // That means data shared by all print objects of the print_objects span may still use the shared data. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0c3bf87b98..bc0370aee5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1990,8 +1990,8 @@ void PrintObject::bridge_over_infill() // reconstruct polygon from polygon sections struct TracedPoly { - std::vector lows; - std::vector highs; + Points lows; + Points highs; }; std::vector current_traced_polys; diff --git a/src/libslic3r/SLA/ConcaveHull.cpp b/src/libslic3r/SLA/ConcaveHull.cpp index 08a2ff676a..f657fce746 100644 --- a/src/libslic3r/SLA/ConcaveHull.cpp +++ b/src/libslic3r/SLA/ConcaveHull.cpp @@ -43,7 +43,8 @@ Point ConcaveHull::centroid(const Points &pp) Points ConcaveHull::calculate_centroids() const { // We get the centroids of all the islands in the 2D slice - Points centroids = reserve_vector(m_polys.size()); + Points centroids; + centroids.reserve(m_polys.size()); std::transform(m_polys.begin(), m_polys.end(), std::back_inserter(centroids), [](const Polygon &poly) { return centroid(poly); }); diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index c84349217e..93259fee86 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -8,10 +8,15 @@ #include #include -namespace ClipperLib { class PolyNode; } +#include namespace Slic3r { + namespace ClipperLib { + class PolyNode; + using PolyNodes = std::vector>; + } + class ExPolygon; using ExPolygons = std::vector; @@ -29,7 +34,7 @@ void chain_and_reorder_extrusion_paths(std::vect Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr); inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); } -std::vector chain_clipper_polynodes(const Points &points, const std::vector &items); +ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items); // Chain instances of print objects by an approximate shortest path. // Returns pairs of PrintObject idx and instance of that PrintObject. diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 856208c9d4..83089fefee 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -106,8 +106,8 @@ enum Axis { NUM_AXES_WITH_UNKNOWN, }; -template -inline void append(std::vector& dest, const std::vector& src) +template +inline void append(std::vector &dest, const std::vector &src) { if (dest.empty()) dest = src; // copy @@ -115,8 +115,8 @@ inline void append(std::vector& dest, const std::vector& src) dest.insert(dest.end(), src.begin(), src.end()); } -template -inline void append(std::vector& dest, std::vector&& src) +template +inline void append(std::vector &dest, std::vector &&src) { if (dest.empty()) dest = std::move(src); diff --git a/tests/libnest2d/CMakeLists.txt b/tests/libnest2d/CMakeLists.txt index ea4f4255ed..8dabc688d3 100644 --- a/tests/libnest2d/CMakeLists.txt +++ b/tests/libnest2d/CMakeLists.txt @@ -2,7 +2,7 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp printer_parts.cpp printer_parts.hpp) # mold linker for successful linking needs also to link TBB library and link it before libslic3r. -target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb libnest2d ) +target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb TBB::tbbmalloc libnest2d ) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") # catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index 239edd4f74..16a27665e8 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -83,7 +83,7 @@ TEST_CASE("Line::perpendicular_to", "[Geometry]") { TEST_CASE("Polygon::contains works properly", "[Geometry]"){ // this test was failing on Windows (GH #1950) - Slic3r::Polygon polygon(std::vector({ + Slic3r::Polygon polygon(Points({ Point(207802834,-57084522), Point(196528149,-37556190), Point(173626821,-25420928), @@ -145,7 +145,7 @@ SCENARIO("polygon_is_convex works") { TEST_CASE("Creating a polyline generates the obvious lines", "[Geometry]"){ Slic3r::Polyline polyline; - polyline.points = std::vector({Point(0, 0), Point(10, 0), Point(20, 0)}); + polyline.points = Points({Point(0, 0), Point(10, 0), Point(20, 0)}); REQUIRE(polyline.lines().at(0).a == Point(0,0)); REQUIRE(polyline.lines().at(0).b == Point(10,0)); REQUIRE(polyline.lines().at(1).a == Point(10,0)); @@ -153,7 +153,7 @@ TEST_CASE("Creating a polyline generates the obvious lines", "[Geometry]"){ } TEST_CASE("Splitting a Polygon generates a polyline correctly", "[Geometry]"){ - Slic3r::Polygon polygon(std::vector({Point(0, 0), Point(10, 0), Point(5, 5)})); + Slic3r::Polygon polygon(Points({Point(0, 0), Point(10, 0), Point(5, 5)})); Slic3r::Polyline split = polygon.split_at_index(1); REQUIRE(split.points[0]==Point(10,0)); REQUIRE(split.points[1]==Point(5,5)); @@ -164,7 +164,7 @@ TEST_CASE("Splitting a Polygon generates a polyline correctly", "[Geometry]"){ SCENARIO("BoundingBox", "[Geometry]") { WHEN("Bounding boxes are scaled") { - BoundingBox bb(std::vector({Point(0, 1), Point(10, 2), Point(20, 2)})); + BoundingBox bb(Points({Point(0, 1), Point(10, 2), Point(20, 2)})); bb.scale(2); REQUIRE(bb.min == Point(0,2)); REQUIRE(bb.max == Point(40,4)); @@ -193,7 +193,7 @@ SCENARIO("BoundingBox", "[Geometry]") { TEST_CASE("Offseting a line generates a polygon correctly", "[Geometry]"){ Slic3r::Polyline tmp = { Point(10,10), Point(20,10) }; Slic3r::Polygon area = offset(tmp,5).at(0); - REQUIRE(area.area() == Slic3r::Polygon(std::vector({Point(10,5),Point(20,5),Point(20,15),Point(10,15)})).area()); + REQUIRE(area.area() == Slic3r::Polygon(Points({Point(10,5),Point(20,5),Point(20,15),Point(10,15)})).area()); } SCENARIO("Circle Fit, TaubinFit with Newton's method", "[Geometry]") { @@ -308,7 +308,7 @@ TEST_CASE("smallest_enclosing_circle_welzl", "[Geometry]") { SCENARIO("Path chaining", "[Geometry]") { GIVEN("A path") { - std::vector points = { Point(26,26),Point(52,26),Point(0,26),Point(26,52),Point(26,0),Point(0,52),Point(52,52),Point(52,0) }; + Points points = { Point(26,26),Point(52,26),Point(0,26),Point(26,52),Point(26,0),Point(0,52),Point(52,52),Point(52,0) }; THEN("Chained with no diagonals (thus 26 units long)") { std::vector indices = chain_points(points); for (Points::size_type i = 0; i + 1 < indices.size(); ++ i) { @@ -431,7 +431,7 @@ SCENARIO("Calculating angles", "[Geometry]") SCENARIO("Polygon convex/concave detection", "[Geometry]"){ static constexpr const double angle_threshold = M_PI / 3.; GIVEN(("A Square with dimension 100")){ - auto square = Slic3r::Polygon /*new_scale*/(std::vector({ + auto square = Slic3r::Polygon /*new_scale*/(Points({ Point(100,100), Point(200,100), Point(200,200), @@ -447,7 +447,7 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ } } GIVEN("A Square with an extra colinearvertex"){ - auto square = Slic3r::Polygon /*new_scale*/(std::vector({ + auto square = Slic3r::Polygon /*new_scale*/(Points({ Point(150,100), Point(200,100), Point(200,200), @@ -459,7 +459,7 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ } } GIVEN("A Square with an extra collinear vertex in different order"){ - auto square = Slic3r::Polygon /*new_scale*/(std::vector({ + auto square = Slic3r::Polygon /*new_scale*/(Points({ Point(200,200), Point(100,200), Point(100,100), @@ -472,7 +472,7 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ } GIVEN("A triangle"){ - auto triangle = Slic3r::Polygon(std::vector({ + auto triangle = Slic3r::Polygon(Points({ Point(16000170,26257364), Point(714223,461012), Point(31286371,461008) @@ -484,7 +484,7 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ } GIVEN("A triangle with an extra collinear point"){ - auto triangle = Slic3r::Polygon(std::vector({ + auto triangle = Slic3r::Polygon(Points({ Point(16000170,26257364), Point(714223,461012), Point(20000000,461012), @@ -498,7 +498,7 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ GIVEN("A polygon with concave vertices with angles of specifically 4/3pi"){ // Two concave vertices of this polygon have angle = PI*4/3, so this test fails // if epsilon is not used. - auto polygon = Slic3r::Polygon(std::vector({ + auto polygon = Slic3r::Polygon(Points({ Point(60246458,14802768),Point(64477191,12360001), Point(63727343,11060995),Point(64086449,10853608), Point(66393722,14850069),Point(66034704,15057334), @@ -516,7 +516,7 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ } TEST_CASE("Triangle Simplification does not result in less than 3 points", "[Geometry]"){ - auto triangle = Slic3r::Polygon(std::vector({ + auto triangle = Slic3r::Polygon(Points({ Point(16000170,26257364), Point(714223,461012), Point(31286371,461008) })); REQUIRE(triangle.simplify(250000).at(0).points.size() == 3); diff --git a/tests/sla_print/CMakeLists.txt b/tests/sla_print/CMakeLists.txt index 2a800cc50e..3a5d96c7ac 100644 --- a/tests/sla_print/CMakeLists.txt +++ b/tests/sla_print/CMakeLists.txt @@ -8,7 +8,7 @@ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp sla_archive_readwrite_tests.cpp) # mold linker for successful linking needs also to link TBB library and link it before libslic3r. -target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb libslic3r) +target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb TBB::tbbmalloc libslic3r) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") if (WIN32) diff --git a/tests/slic3rutils/CMakeLists.txt b/tests/slic3rutils/CMakeLists.txt index 892e7a8a76..7c83fb8d75 100644 --- a/tests/slic3rutils/CMakeLists.txt +++ b/tests/slic3rutils/CMakeLists.txt @@ -6,7 +6,7 @@ add_executable(${_TEST_NAME}_tests ) # mold linker for successful linking needs also to link TBB library and link it before libslic3r. -target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb libslic3r_gui libslic3r) +target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb TBB::tbbmalloc libslic3r_gui libslic3r) if (MSVC) target_link_libraries(${_TEST_NAME}_tests Setupapi.lib) endif () From b67ad6434d1e5b1b54a13e83ceaef4141f9ad35f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 20 Apr 2023 14:30:52 +0200 Subject: [PATCH 02/70] Follow-up to 9cde96993e9f996b44f417570ba05455472efa08 use tbb::scallable_allocator for Polygons and ExPolygon::holes to better scale on multiple threads --- src/clipper/clipper.hpp | 2 +- src/libslic3r/Arachne/WallToolPaths.cpp | 2 +- src/libslic3r/ClipperUtils.hpp | 14 +++++++------- src/libslic3r/JumpPointSearch.cpp | 4 ++-- src/libslic3r/Point.hpp | 6 ++++-- src/libslic3r/Polygon.hpp | 6 +++--- src/libslic3r/SLA/Pad.hpp | 4 +++- src/libslic3r/SLA/SupportTree.hpp | 4 +--- src/libslic3r/ShortestPath.hpp | 4 +--- src/slic3r/GUI/GLModel.hpp | 2 +- src/slic3r/GUI/Jobs/ArrangeJob.cpp | 3 ++- tests/libslic3r/test_clipper_utils.cpp | 4 ++-- 12 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index a2a6712b25..1d63616536 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -281,7 +281,7 @@ enum EdgeSide { esLeft = 1, esRight = 2}; OutPt *Prev; }; - using OutPts = std::vector>; + using OutPts = std::vector>; struct OutRec; struct Join { diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index dd2cda2713..1c69fa9acd 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -341,7 +341,7 @@ void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool re } } else { // For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes - std::vector small_holes; + Polygons small_holes; for (auto it = thiss.begin(); it < new_end;) { if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) { if (area >= 0) { diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 5f495788b0..aaa06107da 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -133,21 +133,21 @@ namespace ClipperUtils { const std::vector &m_paths; }; - template + template class MultiPointsProvider { public: - MultiPointsProvider(const std::vector &multipoints) : m_multipoints(multipoints) {} + MultiPointsProvider(const MultiPointsType &multipoints) : m_multipoints(multipoints) {} struct iterator : public PathsProviderIteratorBase { public: - explicit iterator(typename std::vector::const_iterator it) : m_it(it) {} + explicit iterator(typename MultiPointsType::const_iterator it) : m_it(it) {} const Points& operator*() const { return m_it->points; } bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; } bool operator!=(const iterator &rhs) const { return !(*this == rhs); } const Points& operator++(int) { return (m_it ++)->points; } iterator& operator++() { ++ m_it; return *this; } private: - typename std::vector::const_iterator m_it; + typename MultiPointsType::const_iterator m_it; }; iterator cbegin() const { return iterator(m_multipoints.begin()); } @@ -157,11 +157,11 @@ namespace ClipperUtils { size_t size() const { return m_multipoints.size(); } private: - const std::vector &m_multipoints; + const MultiPointsType &m_multipoints; }; - using PolygonsProvider = MultiPointsProvider; - using PolylinesProvider = MultiPointsProvider; + using PolygonsProvider = MultiPointsProvider; + using PolylinesProvider = MultiPointsProvider; struct ExPolygonProvider { ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {} diff --git a/src/libslic3r/JumpPointSearch.cpp b/src/libslic3r/JumpPointSearch.cpp index 58a656fa04..722415632c 100644 --- a/src/libslic3r/JumpPointSearch.cpp +++ b/src/libslic3r/JumpPointSearch.cpp @@ -269,7 +269,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1) using QNode = astar::QNode>; std::unordered_map astar_cache{}; - std::vector> out_path; + std::vector> out_path; std::vector out_nodes; if (!astar::search_route(tracer, {start, {0, 0}}, std::back_inserter(out_nodes), astar_cache)) { @@ -308,7 +308,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1) svg.draw(scaled_point(start), "green", scale_(0.4)); #endif - std::vector> tmp_path; + std::vector> tmp_path; tmp_path.reserve(out_path.size()); // Some path found, reverse and remove points that do not change direction std::reverse(out_path.begin(), out_path.end()); diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 72c739395d..b482129cf6 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -52,7 +52,9 @@ using Vec2d = Eigen::Matrix; using Vec3d = Eigen::Matrix; using Vec4d = Eigen::Matrix; -using Points = std::vector>; +template +using PointsAllocator = tbb::scalable_allocator; +using Points = std::vector>; using PointPtrs = std::vector; using PointConstPtrs = std::vector; using Points3 = std::vector; @@ -60,7 +62,7 @@ using Pointfs = std::vector; using Vec2ds = std::vector; using Pointf3s = std::vector; -using VecOfPoints = std::vector>; +using VecOfPoints = std::vector>; using Matrix2f = Eigen::Matrix; using Matrix2d = Eigen::Matrix; diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 24a2d69f3a..bf4a087b0d 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -12,9 +12,9 @@ namespace Slic3r { class Polygon; -using Polygons = std::vector; -using PolygonPtrs = std::vector; -using ConstPolygonPtrs = std::vector; +using Polygons = std::vector>; +using PolygonPtrs = std::vector>; +using ConstPolygonPtrs = std::vector>; // Returns true if inside. Returns border_result if on boundary. bool contains(const Polygon& polygon, const Point& p, bool border_result = true); diff --git a/src/libslic3r/SLA/Pad.hpp b/src/libslic3r/SLA/Pad.hpp index 0b61495572..da09343c49 100644 --- a/src/libslic3r/SLA/Pad.hpp +++ b/src/libslic3r/SLA/Pad.hpp @@ -6,6 +6,8 @@ #include #include +#include + struct indexed_triangle_set; namespace Slic3r { @@ -13,7 +15,7 @@ namespace Slic3r { class ExPolygon; class Polygon; using ExPolygons = std::vector; -using Polygons = std::vector; +using Polygons = std::vector>; namespace sla { diff --git a/src/libslic3r/SLA/SupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp index 53fb16f6e7..83814d8c59 100644 --- a/src/libslic3r/SLA/SupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -14,9 +15,6 @@ namespace Slic3r { -using Polygons = std::vector; -using ExPolygons = std::vector; - namespace sla { struct SupportTreeConfig diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index 93259fee86..1781c51889 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -8,13 +8,11 @@ #include #include -#include - namespace Slic3r { namespace ClipperLib { class PolyNode; - using PolyNodes = std::vector>; + using PolyNodes = std::vector>; } class ExPolygon; diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index ef4ab6d474..fbf6ed5334 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -14,7 +14,7 @@ namespace Slic3r { class TriangleMesh; class Polygon; -using Polygons = std::vector; +using Polygons = std::vector>; class BuildVolume; namespace GUI { diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 8115136a57..870957018d 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -183,7 +183,8 @@ static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret, trafo_instance = trafo_instance * po.trafo().cast().inverse(); - auto polys = reserve_vector(3); + Polygons polys; + polys.reserve(3); auto zlvl = -po.get_elevation(); if (omesh) { diff --git a/tests/libslic3r/test_clipper_utils.cpp b/tests/libslic3r/test_clipper_utils.cpp index 775796ba7e..1f3bc0fdca 100644 --- a/tests/libslic3r/test_clipper_utils.cpp +++ b/tests/libslic3r/test_clipper_utils.cpp @@ -308,8 +308,8 @@ SCENARIO("Various Clipper operations - t/clipper.t", "[ClipperUtils]") { } } -template -double polytree_area(const Tree &tree, std::vector

*out) +template +double polytree_area(const Tree &tree, std::vector *out) { traverse_pt(tree, out); From ed65cdd955eca9154ca711bce2c48985af11f8e4 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 20 Apr 2023 18:31:34 +0200 Subject: [PATCH 03/70] Fix compilation of Perl bindings. --- xs/src/xsinit.h | 1 + 1 file changed, 1 insertion(+) diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 19e25c54d1..dcf56a6d49 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -77,6 +77,7 @@ #undef accept #undef wait #undef abort + #undef pause // Breaks compilation with Eigen matrices embedded into Slic3r::Point. #undef malloc From b3b44681a9461550ee148206f329d55c0c9459eb Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 21 Apr 2023 08:31:16 +0200 Subject: [PATCH 04/70] Follow-up after 9cde96993e9f996b44f417570ba05455472efa08 b67ad6434d1e5b1b54a13e83ceaef4141f9ad35f Fixed compilation on GCC and CLang --- src/libslic3r/BoundingBox.cpp | 4 ++-- src/libslic3r/Polyline.hpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp index 10543fc955..7e88eb92df 100644 --- a/src/libslic3r/BoundingBox.cpp +++ b/src/libslic3r/BoundingBox.cpp @@ -160,14 +160,14 @@ template Vec3d BoundingBox3Base::size() const; template double BoundingBoxBase::radius() const { assert(this->defined); - return 0.5 * (this->max - this->min).cast().norm(); + return 0.5 * (this->max - this->min).template cast().norm(); } template double BoundingBoxBase::radius() const; template double BoundingBoxBase::radius() const; template double BoundingBox3Base::radius() const { - return 0.5 * (this->max - this->min).cast().norm(); + return 0.5 * (this->max - this->min).template cast().norm(); } template double BoundingBox3Base::radius() const; diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 9ed5f2137c..8766c6d864 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -161,7 +161,6 @@ inline void polylines_append(Polylines &dst, Polylines &&src) template inline void polylines_merge(PointsType &dst, bool dst_first, PointsType &&src, bool src_first) { - using PointType = typename PointsType::value_type; if (dst_first) { if (src_first) std::reverse(dst.begin(), dst.end()); From 69b69cb9a2a64e8c8b1153a774056808e5463fa6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Apr 2023 09:09:04 +0200 Subject: [PATCH 05/70] Follow-up of 85dd2e486ab22e9d822036749efd75f0e675ccc9 - Fixed Zoom to mouse cursor for orthograpic camera --- src/slic3r/GUI/GLCanvas3D.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 72aaa59448..23079b63c7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2943,8 +2943,8 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) const double delta = direction_factor * (double)evt.GetWheelRotation() / (double)evt.GetWheelDelta(); if (wxGetKeyState(WXK_SHIFT)) { const auto cnv_size = get_canvas_size(); - const auto screen_center_3d_pos = _mouse_to_3d({ cnv_size.get_width() * 0.5, cnv_size.get_height() * 0.5 }); - const auto mouse_3d_pos = _mouse_to_3d({ evt.GetX(), evt.GetY() }); + const Vec3d screen_center_3d_pos = _mouse_to_3d({ cnv_size.get_width() * 0.5, cnv_size.get_height() * 0.5 }); + const Vec3d mouse_3d_pos = _mouse_to_3d({ evt.GetX(), evt.GetY() }); const Vec3d displacement = mouse_3d_pos - screen_center_3d_pos; wxGetApp().plater()->get_camera().translate_world(displacement); const double origin_zoom = wxGetApp().plater()->get_camera().get_zoom(); @@ -6267,7 +6267,8 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) { - return mouse_ray(mouse_pos).intersect_plane(0.0); + const Linef3 ray = mouse_ray(mouse_pos); + return (std::abs(ray.unit_vector().z() < EPSILON)) ? ray.a : ray.intersect_plane(0.0); } void GLCanvas3D::_start_timer() From b04e3bc25e35e1efccb8c85d90d0475dcaa9e102 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Wed, 29 Mar 2023 17:14:03 +0200 Subject: [PATCH 06/70] Initial implementation, requieres both dynamic speed and avoid curled overhangs options to be enabled Also implements new, probably far better estimation of curled height of filament --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 40 ++++++++++++++----- src/libslic3r/SupportSpotsGenerator.cpp | 45 ++++++++++++++++++---- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 625ea695ce..98febf6098 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -246,6 +246,8 @@ class ExtrusionQualityEstimator { std::unordered_map> prev_layer_boundaries; std::unordered_map> next_layer_boundaries; + std::unordered_map> prev_curled_extrusions; + std::unordered_map> next_curled_extrusions; const PrintObject *current_object; public: @@ -253,18 +255,27 @@ public: void prepare_for_new_layer(const Layer *layer) { - if (layer == nullptr) return; - const PrintObject *object = layer->object(); - prev_layer_boundaries[object] = next_layer_boundaries[object]; - next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(layer->lslices)}; + if (layer == nullptr) + return; + const PrintObject *object = layer->object(); + prev_layer_boundaries[object] = next_layer_boundaries[object]; + next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(layer->lslices)}; + prev_curled_extrusions[object] = next_curled_extrusions[object]; + Linesf curled_lines; + curled_lines.reserve(layer->malformed_lines.size()); + for (const Line &l : layer->malformed_lines) { + curled_lines.push_back(Linef(unscaled(l.a), unscaled(l.b))); + } + next_curled_extrusions[object] = AABBTreeLines::LinesDistancer{curled_lines}; } - std::vector estimate_extrusion_quality(const ExtrusionPath &path, - const std::vector> overhangs_w_speeds, - const std::vector> overhangs_w_fan_speeds, - size_t extruder_id, - float ext_perimeter_speed, - float original_speed) + std::vector estimate_speed_from_extrusion_quality( + const ExtrusionPath &path, + const std::vector> overhangs_w_speeds, + const std::vector> overhangs_w_fan_speeds, + size_t extruder_id, + float ext_perimeter_speed, + float original_speed) { float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed; std::map speed_sections; @@ -285,6 +296,15 @@ public: std::vector extended_points = estimate_points_properties(path.polyline.points, prev_layer_boundaries[current_object], path.width); + + for (ExtendedPoint& ep : extended_points) { + // We are going to enforce slowdown by increasing the point distance. The overhang speed is based on signed distance from + // the prev layer, where 0 means fully overlapping extrusions and thus no slowdown, while extrusion_width and more means full overhang, thus full slowdown. + // However, for curling, we take unsinged distance from the curled lines and artifically modifiy the distance + float distance_from_curled = prev_curled_extrusions[current_object].distance_from_lines(ep.position); + ep.distance = std::max(ep.distance, path.width - distance_from_curled); + } + std::vector processed_points; processed_points.reserve(extended_points.size()); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 5062fe18ab..bef3c0fc19 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,7 @@ #include "Geometry/ConvexHull.hpp" // #define DETAILED_DEBUG_LOGS -// #define DEBUG_FILES +#define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -208,16 +209,44 @@ std::vector to_short_lines(const ExtrusionEntity *e, float length float estimate_curled_up_height( const ExtendedPoint &point, float layer_height, float flow_width, float prev_line_curled_height, Params params) { - float curled_up_height = 0.0f; + float curled_up_height = 0; if (fabs(point.distance) < 1.5 * flow_width) { - curled_up_height = 0.85 * prev_line_curled_height; + curled_up_height = 0.9 * prev_line_curled_height; } - if (point.distance > params.malformation_distance_factors.first * flow_width && - point.distance < params.malformation_distance_factors.second * flow_width && point.curvature > -0.1f) { - float dist_factor = std::max(point.distance - params.malformation_distance_factors.first * flow_width, 0.01f) / - ((params.malformation_distance_factors.second - params.malformation_distance_factors.first) * flow_width); - curled_up_height = layer_height * sqrt(sqrt(dist_factor)) * std::clamp(3.0f * point.curvature, 1.0f, 3.0f); + if (point.distance > params.malformation_distance_factors.first * flow_width && + point.distance < params.malformation_distance_factors.second * flow_width) { + // imagine the extrusion profile. The part that has been glued (melted) with the previous layer will be called anchored section + // and the rest will be called curling section + float anchored_section = flow_width - point.distance; + float curling_section = point.distance; + + // after extruding, the curling (floating) part of the extrusion starts to shrink back to the rounded shape of the nozzle + // The anchored part not, because the melted material holds to the previous layer well. + // We can assume for simplicity perfect equalization of layer height and raising part width, from which: + float swelling_radius = (layer_height + curling_section) / 2.0f; + curled_up_height += std::max(0.f, (swelling_radius - layer_height) / 2.0f); + + // There is one more effect. On convex turns, there is larger tension on the floating edge of the extrusion then on the middle section. + // The tension is caused by the shrinking tendency of the filament, and on outer edge of convex trun, the expansion is greater and thus shrinking force is greater. + // This tension will cause the curling section to curle up (Why not down? maybe the previous layer works as a heat block, releasing the heat + // faster or slower than thin air, thus the extrusion always curles up) + + if (point.curvature > 0.01){ + float radius = 1.0 / point.curvature; + // compute radius at the point where the extrusion stops touch previous layer and starts curling + float radius_anchored_section_end = radius - flow_width / 2.0 + anchored_section; + // target radius represents the radius of the extrusion curling end, after curling + // the layer_height term aproximates that the extrusion curling part, when raising to vertical position, will stop before reaching + // perpendicular position, due to various forces. + float target_radius = std::max(radius, radius_anchored_section_end) + layer_height; + + float b = target_radius - radius_anchored_section_end; + float a = (curling_section + swelling_radius) / 2.0; + float c = sqrt(a*a - b*b); + + curled_up_height += c; + } curled_up_height = std::min(curled_up_height, params.max_curled_height_factor * layer_height); } diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 543b9c92bb..c673fc21fb 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -42,7 +42,7 @@ struct Params BrimType brim_type; const float brim_width; - const std::pair malformation_distance_factors = std::pair { 0.5, 1.1 }; + const std::pair malformation_distance_factors = std::pair { 0.3, 0.9 }; const float max_curled_height_factor = 10.0f; const float curling_tolerance_limit = 0.1f; From 3e42d16f622985c840c9f4446917ffde328f62be Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Wed, 29 Mar 2023 18:32:41 +0200 Subject: [PATCH 07/70] minor parameter changes --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 7 +++---- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 98febf6098..bf59543dd5 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -299,13 +299,12 @@ public: for (ExtendedPoint& ep : extended_points) { // We are going to enforce slowdown by increasing the point distance. The overhang speed is based on signed distance from - // the prev layer, where 0 means fully overlapping extrusions and thus no slowdown, while extrusion_width and more means full overhang, thus full slowdown. - // However, for curling, we take unsinged distance from the curled lines and artifically modifiy the distance + // the prev layer, where 0 means fully overlapping extrusions and thus no slowdown, while extrusion_width and more means full overhang, + // thus full slowdown. However, for curling, we take unsinged distance from the curled lines and artifically modifiy the distance float distance_from_curled = prev_curled_extrusions[current_object].distance_from_lines(ep.position); - ep.distance = std::max(ep.distance, path.width - distance_from_curled); + ep.distance = std::max(ep.distance, (path.width - distance_from_curled)); } - std::vector processed_points; processed_points.reserve(extended_points.size()); for (size_t i = 0; i < extended_points.size(); i++) { diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index c673fc21fb..a946159b98 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -42,7 +42,7 @@ struct Params BrimType brim_type; const float brim_width; - const std::pair malformation_distance_factors = std::pair { 0.3, 0.9 }; + const std::pair malformation_distance_factors = std::pair { 0.2, 1.1 }; const float max_curled_height_factor = 10.0f; const float curling_tolerance_limit = 0.1f; From 4ade7d6e8c52773072f6befe8e50beec9425649e Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 31 Mar 2023 12:48:47 +0200 Subject: [PATCH 08/70] Some improvements of the algortihm for curled height estim --- src/libslic3r/SupportSpotsGenerator.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index bef3c0fc19..281d72948f 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -210,8 +210,8 @@ float estimate_curled_up_height( const ExtendedPoint &point, float layer_height, float flow_width, float prev_line_curled_height, Params params) { float curled_up_height = 0; - if (fabs(point.distance) < 1.5 * flow_width) { - curled_up_height = 0.9 * prev_line_curled_height; + if (fabs(point.distance) < 1.1 * flow_width) { + curled_up_height = std::max(prev_line_curled_height - layer_height * 0.5, 0.0); } if (point.distance > params.malformation_distance_factors.first * flow_width && @@ -233,17 +233,17 @@ float estimate_curled_up_height( // faster or slower than thin air, thus the extrusion always curles up) if (point.curvature > 0.01){ - float radius = 1.0 / point.curvature; - // compute radius at the point where the extrusion stops touch previous layer and starts curling - float radius_anchored_section_end = radius - flow_width / 2.0 + anchored_section; + float radius = std::max(1.0 / point.curvature - flow_width / 2.0, 0.001); + // compute radius at the point where the extrusion stops touching the previous layer and starts curling + float radius_anchored_section_end = radius + anchored_section; // target radius represents the radius of the extrusion curling end, after curling // the layer_height term aproximates that the extrusion curling part, when raising to vertical position, will stop before reaching // perpendicular position, due to various forces. - float target_radius = std::max(radius, radius_anchored_section_end) + layer_height; + float target_radius = radius_anchored_section_end + radius * flow_width / 100.0; float b = target_radius - radius_anchored_section_end; - float a = (curling_section + swelling_radius) / 2.0; - float c = sqrt(a*a - b*b); + float a = curling_section; + float c = sqrt(std::max(0.0f,a*a - b*b)); curled_up_height += c; } From 6ee674316dd76fc74fd4007fb4e5fcd3fa8dce33 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 3 Apr 2023 10:10:15 +0200 Subject: [PATCH 09/70] Finally some working curling model --- src/libslic3r/SupportSpotsGenerator.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 281d72948f..ee62b1e6ce 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -218,7 +218,7 @@ float estimate_curled_up_height( point.distance < params.malformation_distance_factors.second * flow_width) { // imagine the extrusion profile. The part that has been glued (melted) with the previous layer will be called anchored section // and the rest will be called curling section - float anchored_section = flow_width - point.distance; + // float anchored_section = flow_width - point.distance; float curling_section = point.distance; // after extruding, the curling (floating) part of the extrusion starts to shrink back to the rounded shape of the nozzle @@ -234,14 +234,8 @@ float estimate_curled_up_height( if (point.curvature > 0.01){ float radius = std::max(1.0 / point.curvature - flow_width / 2.0, 0.001); - // compute radius at the point where the extrusion stops touching the previous layer and starts curling - float radius_anchored_section_end = radius + anchored_section; - // target radius represents the radius of the extrusion curling end, after curling - // the layer_height term aproximates that the extrusion curling part, when raising to vertical position, will stop before reaching - // perpendicular position, due to various forces. - float target_radius = radius_anchored_section_end + radius * flow_width / 100.0; - - float b = target_radius - radius_anchored_section_end; + float curling_t = radius / 100; + float b = curling_t * flow_width; float a = curling_section; float c = sqrt(std::max(0.0f,a*a - b*b)); From 6e40e061f6fa057767ffe2a7916f8e175c48e4f6 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 3 Apr 2023 15:14:07 +0200 Subject: [PATCH 10/70] Finish rough implementation of slowdown over curled filament --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 33 ++++++++++++---------- src/libslic3r/JumpPointSearch.cpp | 4 +-- src/libslic3r/Layer.hpp | 3 +- src/libslic3r/Line.hpp | 12 ++++++++ src/libslic3r/SupportSpotsGenerator.cpp | 12 ++++---- 5 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index bf59543dd5..ffa351c27e 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -13,6 +13,7 @@ #include "../ClipperUtils.hpp" #include "../Flow.hpp" #include "../Config.hpp" +#include "../Line.hpp" #include #include @@ -246,8 +247,8 @@ class ExtrusionQualityEstimator { std::unordered_map> prev_layer_boundaries; std::unordered_map> next_layer_boundaries; - std::unordered_map> prev_curled_extrusions; - std::unordered_map> next_curled_extrusions; + std::unordered_map> prev_curled_extrusions; + std::unordered_map> next_curled_extrusions; const PrintObject *current_object; public: @@ -261,12 +262,7 @@ public: prev_layer_boundaries[object] = next_layer_boundaries[object]; next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(layer->lslices)}; prev_curled_extrusions[object] = next_curled_extrusions[object]; - Linesf curled_lines; - curled_lines.reserve(layer->malformed_lines.size()); - for (const Line &l : layer->malformed_lines) { - curled_lines.push_back(Linef(unscaled(l.a), unscaled(l.b))); - } - next_curled_extrusions[object] = AABBTreeLines::LinesDistancer{curled_lines}; + next_curled_extrusions[object] = AABBTreeLines::LinesDistancer{layer->curled_lines}; } std::vector estimate_speed_from_extrusion_quality( @@ -296,13 +292,20 @@ public: std::vector extended_points = estimate_points_properties(path.polyline.points, prev_layer_boundaries[current_object], path.width); - - for (ExtendedPoint& ep : extended_points) { - // We are going to enforce slowdown by increasing the point distance. The overhang speed is based on signed distance from - // the prev layer, where 0 means fully overlapping extrusions and thus no slowdown, while extrusion_width and more means full overhang, - // thus full slowdown. However, for curling, we take unsinged distance from the curled lines and artifically modifiy the distance - float distance_from_curled = prev_curled_extrusions[current_object].distance_from_lines(ep.position); - ep.distance = std::max(ep.distance, (path.width - distance_from_curled)); + + for (ExtendedPoint &ep : extended_points) { + // We are going to enforce slowdown over curled extrusions by increasing the point distance. The overhang speed is based on + // signed distance from the prev layer, where 0 means fully overlapping extrusions and thus no slowdown, while extrusion_width + // and more means full overhang, thus full slowdown. However, for curling, we take unsinged distance from the curled lines and + // artifically modifiy the distance + auto [distance_from_curled, line_idx, + p] = prev_curled_extrusions[current_object].distance_from_lines_extra(Point::new_scale(ep.position)); + if (distance_from_curled < scale_(2.0 * path.width)) { + float articifally_increased_distance = path.width * + prev_curled_extrusions[current_object].get_line(line_idx).curled_height / + (path.height * 10.0f); // max_curled_height_factor from SupportSpotGenerator + ep.distance = std::max(ep.distance, articifally_increased_distance); + } } std::vector processed_points; diff --git a/src/libslic3r/JumpPointSearch.cpp b/src/libslic3r/JumpPointSearch.cpp index ef3dba45e7..dbae42151b 100644 --- a/src/libslic3r/JumpPointSearch.cpp +++ b/src/libslic3r/JumpPointSearch.cpp @@ -211,8 +211,8 @@ void JPSPathFinder::add_obstacles(const Layer *layer, const Point &global_origin this->print_z = layer->print_z; Lines obstacles; - obstacles.reserve(layer->malformed_lines.size()); - for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); } + obstacles.reserve(layer->curled_lines.size()); + for (const Line &l : layer->curled_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); } add_obstacles(obstacles); } diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index b3d071c9d2..5cfdf9cfad 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_Layer_hpp_ #define slic3r_Layer_hpp_ +#include "Line.hpp" #include "libslic3r.h" #include "BoundingBox.hpp" #include "Flow.hpp" @@ -325,7 +326,7 @@ public: coordf_t bottom_z() const { return this->print_z - this->height; } //Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels. - Lines malformed_lines; + CurledLines curled_lines; // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // (with possibly differing extruder ID and slicing parameters) and merged. diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 90f5648980..1edd1f5e9f 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -209,6 +209,18 @@ public: double a_width, b_width; }; +class CurledLine : public Line +{ +public: + CurledLine() : curled_height(0.0f) {} + CurledLine(const Point& a, const Point& b) : Line(a, b), curled_height(0.0f) {} + CurledLine(const Point& a, const Point& b, float curled_height) : Line(a, b), curled_height(curled_height) {} + + float curled_height; +}; + +using CurledLines = std::vector; + class Line3 { public: diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index ee62b1e6ce..71ea7264d0 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -39,7 +39,7 @@ #include "Geometry/ConvexHull.hpp" // #define DETAILED_DEBUG_LOGS -#define DEBUG_FILES +// #define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -234,7 +234,7 @@ float estimate_curled_up_height( if (point.curvature > 0.01){ float radius = std::max(1.0 / point.curvature - flow_width / 2.0, 0.001); - float curling_t = radius / 100; + float curling_t = sqrt(radius / 100); float b = curling_t * flow_width; float a = curling_section; float c = sqrt(std::max(0.0f,a*a - b*b)); @@ -1066,7 +1066,7 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, AABBTreeLines::LinesDistancer prev_layer_lines{}; for (SupportLayer *l : layers) { - l->malformed_lines.clear(); + l->curled_lines.clear(); std::vector current_layer_lines; for (const ExtrusionEntity *extrusion : l->support_fills.flatten().entities) { @@ -1102,7 +1102,7 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, for (const ExtrusionLine &line : current_layer_lines) { if (line.curled_up_height > params.curling_tolerance_limit) { - l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)}); + l->curled_lines.push_back(CurledLine{Point::new_scale(line.a), Point::new_scale(line.b), line.curled_up_height}); } } @@ -1138,7 +1138,7 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) LD prev_layer_lines{}; for (Layer *l : layers) { - l->malformed_lines.clear(); + l->curled_lines.clear(); std::vector boundary_lines = l->lower_layer != nullptr ? to_unscaled_linesf(l->lower_layer->lslices) : std::vector(); AABBTreeLines::LinesDistancer prev_layer_boundary{std::move(boundary_lines)}; std::vector current_layer_lines; @@ -1176,7 +1176,7 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) for (const ExtrusionLine &line : current_layer_lines) { if (line.curled_up_height > params.curling_tolerance_limit) { - l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)}); + l->curled_lines.push_back(CurledLine{Point::new_scale(line.a), Point::new_scale(line.b), line.curled_up_height}); } } From da6b972a79843ab5886d67dd5959e6a503313548 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 11 Apr 2023 13:01:31 +0200 Subject: [PATCH 11/70] Smoothen the curled height estimations and slowdown --- src/libslic3r/AABBTreeLines.hpp | 2 +- src/libslic3r/GCode.cpp | 2 +- src/libslic3r/GCode/ExtrusionProcessor.hpp | 45 ++++++++++++++++------ src/libslic3r/SupportSpotsGenerator.cpp | 44 ++++++++++++++++----- 4 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 2136e8edbd..ae5569b517 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -347,7 +347,7 @@ public: 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); + return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point, radius * radius); } template std::vector, size_t>> intersections_with_line(const LineType &line) const diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 29316a9de5..357071a615 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3036,7 +3036,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm); } - new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds, + new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds, m_writer.extruder()->id(), external_perim_reference_speed, speed); variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(), diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index ffa351c27e..81a1c018ab 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -101,13 +101,10 @@ public: struct ExtendedPoint { - ExtendedPoint(Vec2d position, float distance = 0.0, size_t nearest_prev_layer_line = size_t(-1), float curvature = 0.0) - : position(position), distance(distance), nearest_prev_layer_line(nearest_prev_layer_line), curvature(curvature) - {} - Vec2d position; float distance; size_t nearest_prev_layer_line; + Vec2d nearest_prev_layer_point; float curvature; }; @@ -132,6 +129,7 @@ std::vector estimate_points_properties(const std::vector

auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast()); start_point.distance = distance + boundary_offset; start_point.nearest_prev_layer_line = nearest_line; + start_point.nearest_prev_layer_point = x.template cast(); points.push_back(start_point); } for (size_t i = 1; i < input_points.size(); i++) { @@ -139,13 +137,19 @@ std::vector estimate_points_properties(const std::vector

auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast()); next_point.distance = distance + boundary_offset; next_point.nearest_prev_layer_line = nearest_line; + next_point.nearest_prev_layer_point = x.template cast(); if (ADD_INTERSECTIONS && ((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) { const ExtendedPoint &prev_point = points.back(); auto intersections = unscaled_prev_layer.template intersections_with_line(L{prev_point.position.cast(), next_point.position.cast()}); for (const auto &intersection : intersections) { - points.emplace_back(intersection.first.template cast(), boundary_offset, intersection.second); + ExtendedPoint p{}; + p.position = intersection.first.template cast(); + p.distance = boundary_offset; + p.nearest_prev_layer_line = intersection.second; + p.nearest_prev_layer_point = p.position; + points.push_back(p); } } points.push_back(next_point); @@ -171,12 +175,22 @@ std::vector estimate_points_properties(const std::vector

if (t0 < 1.0) { auto p0 = curr.position + t0 * (next.position - curr.position); auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra(p0.cast()); - new_points.push_back(ExtendedPoint{p0, float(p0_dist + boundary_offset), p0_near_l}); + ExtendedPoint new_p{}; + new_p.position = p0; + new_p.distance = float(p0_dist + boundary_offset); + new_p.nearest_prev_layer_line = p0_near_l; + new_p.nearest_prev_layer_point = p0_x.template cast(); + new_points.push_back(new_p); } if (t1 > 0.0) { auto p1 = curr.position + t1 * (next.position - curr.position); auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra(p1.cast()); - new_points.push_back(ExtendedPoint{p1, float(p1_dist + boundary_offset), p1_near_l}); + ExtendedPoint new_p{}; + new_p.position = p1; + new_p.distance = float(p1_dist + boundary_offset); + new_p.nearest_prev_layer_line = p1_near_l; + new_p.nearest_prev_layer_point = p1_x.template cast(); + new_points.push_back(new_p); } } } @@ -200,7 +214,12 @@ std::vector estimate_points_properties(const std::vector

Vec2d pos = curr.position * (1.0 - j * t) + next.position * (j * t); auto [p_dist, p_near_l, p_x] = unscaled_prev_layer.template distance_from_lines_extra(pos.cast()); - new_points.push_back(ExtendedPoint{pos, float(p_dist + boundary_offset), p_near_l}); + ExtendedPoint new_p{}; + new_p.position = pos; + new_p.distance = float(p_dist + boundary_offset); + new_p.nearest_prev_layer_line = p_near_l; + new_p.nearest_prev_layer_point = p_x.template cast(); + new_points.push_back(new_p); } } new_points.push_back(points.back()); @@ -301,10 +320,12 @@ public: auto [distance_from_curled, line_idx, p] = prev_curled_extrusions[current_object].distance_from_lines_extra(Point::new_scale(ep.position)); if (distance_from_curled < scale_(2.0 * path.width)) { - float articifally_increased_distance = path.width * - prev_curled_extrusions[current_object].get_line(line_idx).curled_height / - (path.height * 10.0f); // max_curled_height_factor from SupportSpotGenerator - ep.distance = std::max(ep.distance, articifally_increased_distance); + float artificially_increased_distance = path.width * + (1.0 - (unscaled(distance_from_curled) / (2.0 * path.width)) * + (unscaled(distance_from_curled) / (2.0 * path.width))) * + (prev_curled_extrusions[current_object].get_line(line_idx).curled_height / + (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator + ep.distance = std::max(ep.distance, artificially_increased_distance); } } diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 71ea7264d0..15a4aa535f 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -13,6 +13,7 @@ #include "PrintBase.hpp" #include "PrintConfig.hpp" #include "Tesselate.hpp" +#include "Utils.hpp" #include "libslic3r.h" #include "tbb/parallel_for.h" #include "tbb/blocked_range.h" @@ -247,6 +248,32 @@ float estimate_curled_up_height( return curled_up_height; } +std::tuple get_bottom_extrusions_quality_and_curling(const LD &prev_layer_lines, const ExtendedPoint &curr_point) +{ + if (prev_layer_lines.get_lines().empty()) { + return {1.0,0.0}; + } + const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_line(curr_point.nearest_prev_layer_line); + float quality = nearest_prev_layer_line.form_quality; + float curling = nearest_prev_layer_line.curled_up_height; + if ((curr_point.nearest_prev_layer_point.cast() - nearest_prev_layer_line.a).squaredNorm() < 0.1) { + const auto& prev_line = prev_layer_lines.get_line(prev_idx_modulo(curr_point.nearest_prev_layer_line, prev_layer_lines.get_lines().size())); + if ((curr_point.nearest_prev_layer_point.cast() - prev_line.b).squaredNorm() < 0.1) { + quality = 0.5 * (quality + prev_line.form_quality); + curling = 0.5 * (curling + prev_line.curled_up_height); + } + } else if ((curr_point.nearest_prev_layer_point.cast() - nearest_prev_layer_line.b).squaredNorm() < 0.1) { + const auto &next_line = prev_layer_lines.get_line( + next_idx_modulo(curr_point.nearest_prev_layer_line, prev_layer_lines.get_lines().size())); + if ((curr_point.nearest_prev_layer_point.cast() - next_line.a).squaredNorm() < 0.1) { + quality = 0.5 * (quality + next_line.form_quality); + curling = 0.5 * (curling + next_line.curled_up_height); + } + } + + return {quality, curling}; +} + std::vector check_extrusion_entity_stability(const ExtrusionEntity *entity, const LayerRegion *layer_region, const LD &prev_layer_lines, @@ -335,9 +362,7 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit float line_len = (prev_point.position - curr_point.position).norm(); ExtrusionLine line_out{prev_point.position.cast(), curr_point.position.cast(), line_len, entity}; - const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? - prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : - ExtrusionLine{}; + auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, curr_point); // correctify the distance sign using slice polygons float sign = (prev_layer_boundary.distance_from_lines(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; @@ -362,7 +387,7 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit } } else if (curr_point.distance > flow_width * 0.8f) { bridged_distance += line_len; - line_out.form_quality = nearest_prev_layer_line.form_quality - 0.3f; + line_out.form_quality = prev_layer_quality - 0.3f; if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) { line_out.support_point_generated = potential_cause; line_out.form_quality = 0.5f; @@ -373,7 +398,7 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit } line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width, - nearest_prev_layer_line.curled_up_height, params); + prev_layer_curling, params); lines_out.push_back(line_out); } @@ -1082,6 +1107,7 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast() : curr_point.position.cast(), curr_point.position.cast(), line_len, extrusion}; + auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, curr_point); const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : ExtrusionLine{}; @@ -1094,7 +1120,7 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, } line_out.curled_up_height = estimate_curled_up_height(curr_point, l->height, flow_width, - nearest_prev_layer_line.curled_up_height, params); + prev_layer_curling, params); current_layer_lines.push_back(line_out); } @@ -1158,16 +1184,14 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast() : curr_point.position.cast(), curr_point.position.cast(), line_len, extrusion}; - const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? - prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : - ExtrusionLine{}; + auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, curr_point); float sign = (prev_layer_boundary.distance_from_lines(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; curr_point.distance *= sign; line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width, - nearest_prev_layer_line.curled_up_height, params); + prev_layer_curling, params); current_layer_lines.push_back(line_out); } From aa0e21eed1f6eda7ce93f26821edb5cc8bcbbf7c Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 11 Apr 2023 16:40:14 +0200 Subject: [PATCH 12/70] Center estiamted curvature values, Center esimtated curling - transfering data by nearest line caused CCW or CW shift, based on which point of the current line was used. By switching the points on layer basis, this problem disapeared --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 127 ++++++++------------- src/libslic3r/SupportSpotsGenerator.cpp | 60 +++++----- 2 files changed, 77 insertions(+), 110 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 81a1c018ab..6e441a4c61 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -27,78 +27,6 @@ namespace Slic3r { -class SlidingWindowCurvatureAccumulator -{ - float window_size; - float total_distance = 0; // accumulated distance - float total_curvature = 0; // accumulated signed ccw angles - deque distances; - deque angles; - -public: - SlidingWindowCurvatureAccumulator(float window_size) : window_size(window_size) {} - - void add_point(float distance, float angle) - { - total_distance += distance; - total_curvature += angle; - distances.push_back(distance); - angles.push_back(angle); - - while (distances.size() > 1 && total_distance > window_size) { - total_distance -= distances.front(); - total_curvature -= angles.front(); - distances.pop_front(); - angles.pop_front(); - } - } - - float get_curvature() const - { - return total_curvature / window_size; - } - - void reset() - { - total_curvature = 0; - total_distance = 0; - distances.clear(); - angles.clear(); - } -}; - -class CurvatureEstimator -{ - static const size_t sliders_count = 3; - SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{1.0},{4.0}, {10.0}}; - -public: - void add_point(float distance, float angle) - { - if (distance < EPSILON) - return; - for (SlidingWindowCurvatureAccumulator &slider : sliders) { - slider.add_point(distance, angle); - } - } - float get_curvature() - { - float max_curvature = 0.0f; - for (const SlidingWindowCurvatureAccumulator &slider : sliders) { - if (abs(slider.get_curvature()) > abs(max_curvature)) { - max_curvature = slider.get_curvature(); - } - } - return max_curvature; - } - void reset() - { - for (SlidingWindowCurvatureAccumulator &slider : sliders) { - slider.reset(); - } - } -}; - struct ExtendedPoint { Vec2d position; @@ -118,7 +46,6 @@ std::vector estimate_points_properties(const std::vector

if (input_points.empty()) return {}; float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; - CurvatureEstimator cestim; auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; std::vector points; @@ -227,6 +154,9 @@ std::vector estimate_points_properties(const std::vector

points = new_points; } + std::vector angles_for_curvature(points.size()); + std::vector distances_for_curvature(points.size()); + for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) { ExtendedPoint &a = points[point_idx]; ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx]; @@ -234,22 +164,59 @@ std::vector estimate_points_properties(const std::vector

int prev_point_idx = point_idx; while (prev_point_idx > 0) { prev_point_idx--; - if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { break; } + if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { + break; + } } int next_point_index = point_idx; while (next_point_index < int(points.size()) - 1) { next_point_index++; - if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) { break; } + if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) { + break; + } } + distances_for_curvature[point_idx] = (prev.position - a.position).norm(); if (prev_point_idx != point_idx && next_point_index != point_idx) { - float distance = (prev.position - a.position).norm(); - float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position); - cestim.add_point(distance, alfa); - } + float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position); + angles_for_curvature[point_idx] = alfa; + } // else keep zero + } - a.curvature = cestim.get_curvature(); + for (float window_size : {3.0f, 9.0f, 16.0f}) { + size_t tail_point = 0; + float tail_window_acc = 0; + float tail_angle_acc = 0; + + size_t head_point = 0; + float head_window_acc = 0; + float head_angle_acc = 0; + + for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) { + if (point_idx > 0) { + tail_window_acc += distances_for_curvature[point_idx - 1]; + tail_angle_acc += angles_for_curvature[point_idx - 1]; + head_window_acc -= distances_for_curvature[point_idx - 1]; + head_angle_acc -= angles_for_curvature[point_idx - 1]; + } + while (tail_window_acc > window_size * 0.5 && tail_point < point_idx) { + tail_window_acc -= distances_for_curvature[tail_point]; + tail_angle_acc -= angles_for_curvature[tail_point]; + tail_point++; + } + + while (head_window_acc < window_size * 0.5 && head_point < int(points.size()) - 1) { + head_window_acc += distances_for_curvature[head_point]; + head_angle_acc += angles_for_curvature[head_point]; + head_point++; + } + + float curvature = (tail_angle_acc + head_angle_acc) / (tail_window_acc + head_window_acc); + if (std::abs(curvature) > std::abs(points[point_idx].curvature)) { + points[point_idx].curvature = curvature; + } + } } return points; diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 15a4aa535f..0ad76d8b12 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -234,7 +234,7 @@ float estimate_curled_up_height( // faster or slower than thin air, thus the extrusion always curles up) if (point.curvature > 0.01){ - float radius = std::max(1.0 / point.curvature - flow_width / 2.0, 0.001); + float radius = (1.0 / point.curvature); float curling_t = sqrt(radius / 100); float b = curling_t * flow_width; float a = curling_section; @@ -397,8 +397,8 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit bridged_distance = 0.0f; } - line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width, - prev_layer_curling, params); + line_out.curled_up_height = estimate_curled_up_height(layer_region->layer()->id() % 2 == 0 ? curr_point : prev_point, + layer_region->layer()->height, flow_width, prev_layer_curling, params); lines_out.push_back(line_out); } @@ -1102,25 +1102,25 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, auto annotated_points = estimate_points_properties(pol.points, prev_layer_lines, flow_width); for (size_t i = 0; i < annotated_points.size(); ++i) { - ExtendedPoint &curr_point = annotated_points[i]; - float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f; - ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast() : curr_point.position.cast(), - curr_point.position.cast(), line_len, extrusion}; + ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + ExtendedPoint &b = annotated_points[i]; + ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), + extrusion}; - auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, curr_point); - const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? - prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : - ExtrusionLine{}; + ExtendedPoint &pivot = l->id() % 2 == 0 ? a : b; + auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, pivot); + const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? + prev_layer_lines.get_line(pivot.nearest_prev_layer_line) : + ExtrusionLine{}; Vec2f v1 = (nearest_prev_layer_line.b - nearest_prev_layer_line.a); - Vec2f v2 = (curr_point.position.cast() - nearest_prev_layer_line.a); + Vec2f v2 = (pivot.position.cast() - nearest_prev_layer_line.a); auto d = (v1.x() * v2.y()) - (v1.y() * v2.x()); if (d > 0) { - curr_point.distance *= -1.0f; + pivot.distance *= -1.0f; } - line_out.curled_up_height = estimate_curled_up_height(curr_point, l->height, flow_width, - prev_layer_curling, params); + line_out.curled_up_height = estimate_curled_up_height(pivot, l->height, flow_width, prev_layer_curling, params); current_layer_lines.push_back(line_out); } @@ -1158,7 +1158,7 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) { #ifdef DEBUG_FILES FILE *debug_file = boost::nowide::fopen(debug_out_path("object_malformations.obj").c_str(), "w"); - FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w"); + FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w"); #endif LD prev_layer_lines{}; @@ -1170,8 +1170,8 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) std::vector current_layer_lines; for (const LayerRegion *layer_region : l->regions()) { for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) { - - if (!extrusion->role().is_external_perimeter()) continue; + if (!extrusion->role().is_external_perimeter()) + continue; Points extrusion_pts; extrusion->collect_points(extrusion_pts); @@ -1179,18 +1179,18 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) auto annotated_points = estimate_points_properties(extrusion_pts, prev_layer_lines, flow_width, params.bridge_distance); for (size_t i = 0; i < annotated_points.size(); ++i) { - ExtendedPoint &curr_point = annotated_points[i]; - float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f; - ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast() : curr_point.position.cast(), - curr_point.position.cast(), line_len, extrusion}; + ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + ExtendedPoint &b = annotated_points[i]; + ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), + extrusion}; - auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, curr_point); + ExtendedPoint &pivot = l->id() % 2 == 0 ? a : b; + auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, pivot); - float sign = (prev_layer_boundary.distance_from_lines(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : - 1.0f; - curr_point.distance *= sign; + float sign = (prev_layer_boundary.distance_from_lines(pivot.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; + pivot.distance *= sign; - line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width, + line_out.curled_up_height = estimate_curled_up_height(pivot, layer_region->layer()->height, flow_width, prev_layer_curling, params); current_layer_lines.push_back(line_out); @@ -1211,9 +1211,9 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]); } } - for (const ExtrusionLine &line : current_layer_lines) { - Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height); - fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]); + for (const ExtrusionLine &line : current_layer_lines) { + Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height); + fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]); } #endif From 798396d9181ed5402cc377d868caf03de2689885 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 12 Apr 2023 17:24:11 +0200 Subject: [PATCH 13/70] Fixed several issues with smoothening of the slowdown, but there are still artefacts in the preview, on curved into flat srufaces --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 20 +--- src/libslic3r/SupportSpotsGenerator.cpp | 122 +++++++++------------ 2 files changed, 53 insertions(+), 89 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 6e441a4c61..d79fee83a0 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -31,8 +31,6 @@ struct ExtendedPoint { Vec2d position; float distance; - size_t nearest_prev_layer_line; - Vec2d nearest_prev_layer_point; float curvature; }; @@ -55,16 +53,12 @@ std::vector estimate_points_properties(const std::vector

ExtendedPoint start_point{maybe_unscale(input_points.front())}; auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast()); start_point.distance = distance + boundary_offset; - start_point.nearest_prev_layer_line = nearest_line; - start_point.nearest_prev_layer_point = x.template cast(); points.push_back(start_point); } for (size_t i = 1; i < input_points.size(); i++) { ExtendedPoint next_point{maybe_unscale(input_points[i])}; auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast()); next_point.distance = distance + boundary_offset; - next_point.nearest_prev_layer_line = nearest_line; - next_point.nearest_prev_layer_point = x.template cast(); if (ADD_INTERSECTIONS && ((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) { @@ -74,8 +68,6 @@ std::vector estimate_points_properties(const std::vector

ExtendedPoint p{}; p.position = intersection.first.template cast(); p.distance = boundary_offset; - p.nearest_prev_layer_line = intersection.second; - p.nearest_prev_layer_point = p.position; points.push_back(p); } } @@ -105,8 +97,6 @@ std::vector estimate_points_properties(const std::vector

ExtendedPoint new_p{}; new_p.position = p0; new_p.distance = float(p0_dist + boundary_offset); - new_p.nearest_prev_layer_line = p0_near_l; - new_p.nearest_prev_layer_point = p0_x.template cast(); new_points.push_back(new_p); } if (t1 > 0.0) { @@ -115,8 +105,6 @@ std::vector estimate_points_properties(const std::vector

ExtendedPoint new_p{}; new_p.position = p1; new_p.distance = float(p1_dist + boundary_offset); - new_p.nearest_prev_layer_line = p1_near_l; - new_p.nearest_prev_layer_point = p1_x.template cast(); new_points.push_back(new_p); } } @@ -144,8 +132,6 @@ std::vector estimate_points_properties(const std::vector

ExtendedPoint new_p{}; new_p.position = pos; new_p.distance = float(p_dist + boundary_offset); - new_p.nearest_prev_layer_line = p_near_l; - new_p.nearest_prev_layer_point = p_x.template cast(); new_points.push_back(new_p); } } @@ -279,13 +265,15 @@ public: std::vector extended_points = estimate_points_properties(path.polyline.points, prev_layer_boundaries[current_object], path.width); - for (ExtendedPoint &ep : extended_points) { + for (size_t i = 0; i < int(extended_points.size()) - 1; i++) { + ExtendedPoint& ep = extended_points[i]; // We are going to enforce slowdown over curled extrusions by increasing the point distance. The overhang speed is based on // signed distance from the prev layer, where 0 means fully overlapping extrusions and thus no slowdown, while extrusion_width // and more means full overhang, thus full slowdown. However, for curling, we take unsinged distance from the curled lines and // artifically modifiy the distance + Vec2d middle = 0.5 * (ep.position + extended_points[i + 1].position); auto [distance_from_curled, line_idx, - p] = prev_curled_extrusions[current_object].distance_from_lines_extra(Point::new_scale(ep.position)); + p] = prev_curled_extrusions[current_object].distance_from_lines_extra(Point::new_scale(middle)); if (distance_from_curled < scale_(2.0 * path.width)) { float artificially_increased_distance = path.width * (1.0 - (unscaled(distance_from_curled) / (2.0 * path.width)) * diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 0ad76d8b12..e9f60dabde 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,7 @@ #include "Geometry/ConvexHull.hpp" // #define DETAILED_DEBUG_LOGS -// #define DEBUG_FILES +#define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -208,19 +209,19 @@ std::vector to_short_lines(const ExtrusionEntity *e, float length } float estimate_curled_up_height( - const ExtendedPoint &point, float layer_height, float flow_width, float prev_line_curled_height, Params params) + float distance, float curvature, float layer_height, float flow_width, float prev_line_curled_height, Params params) { float curled_up_height = 0; - if (fabs(point.distance) < 1.1 * flow_width) { - curled_up_height = std::max(prev_line_curled_height - layer_height * 0.5, 0.0); + if (fabs(distance) < 2.5 * flow_width) { + curled_up_height = std::max(prev_line_curled_height - layer_height * 0.75f, 0.0f); } - if (point.distance > params.malformation_distance_factors.first * flow_width && - point.distance < params.malformation_distance_factors.second * flow_width) { + if (distance > params.malformation_distance_factors.first * flow_width && + distance < params.malformation_distance_factors.second * flow_width) { // imagine the extrusion profile. The part that has been glued (melted) with the previous layer will be called anchored section // and the rest will be called curling section // float anchored_section = flow_width - point.distance; - float curling_section = point.distance; + float curling_section = distance; // after extruding, the curling (floating) part of the extrusion starts to shrink back to the rounded shape of the nozzle // The anchored part not, because the melted material holds to the previous layer well. @@ -228,17 +229,15 @@ float estimate_curled_up_height( float swelling_radius = (layer_height + curling_section) / 2.0f; curled_up_height += std::max(0.f, (swelling_radius - layer_height) / 2.0f); - // There is one more effect. On convex turns, there is larger tension on the floating edge of the extrusion then on the middle section. - // The tension is caused by the shrinking tendency of the filament, and on outer edge of convex trun, the expansion is greater and thus shrinking force is greater. - // This tension will cause the curling section to curle up (Why not down? maybe the previous layer works as a heat block, releasing the heat - // faster or slower than thin air, thus the extrusion always curles up) - - if (point.curvature > 0.01){ - float radius = (1.0 / point.curvature); + // On convex turns, there is larger tension on the floating edge of the extrusion then on the middle section. + // The tension is caused by the shrinking tendency of the filament, and on outer edge of convex trun, the expansion is greater and + // thus shrinking force is greater. This tension will cause the curling section to curle up + if (curvature > 0.01) { + float radius = (1.0 / curvature); float curling_t = sqrt(radius / 100); - float b = curling_t * flow_width; - float a = curling_section; - float c = sqrt(std::max(0.0f,a*a - b*b)); + float b = curling_t * flow_width; + float a = curling_section; + float c = sqrt(std::max(0.0f, a * a - b * b)); curled_up_height += c; } @@ -248,32 +247,6 @@ float estimate_curled_up_height( return curled_up_height; } -std::tuple get_bottom_extrusions_quality_and_curling(const LD &prev_layer_lines, const ExtendedPoint &curr_point) -{ - if (prev_layer_lines.get_lines().empty()) { - return {1.0,0.0}; - } - const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_line(curr_point.nearest_prev_layer_line); - float quality = nearest_prev_layer_line.form_quality; - float curling = nearest_prev_layer_line.curled_up_height; - if ((curr_point.nearest_prev_layer_point.cast() - nearest_prev_layer_line.a).squaredNorm() < 0.1) { - const auto& prev_line = prev_layer_lines.get_line(prev_idx_modulo(curr_point.nearest_prev_layer_line, prev_layer_lines.get_lines().size())); - if ((curr_point.nearest_prev_layer_point.cast() - prev_line.b).squaredNorm() < 0.1) { - quality = 0.5 * (quality + prev_line.form_quality); - curling = 0.5 * (curling + prev_line.curled_up_height); - } - } else if ((curr_point.nearest_prev_layer_point.cast() - nearest_prev_layer_line.b).squaredNorm() < 0.1) { - const auto &next_line = prev_layer_lines.get_line( - next_idx_modulo(curr_point.nearest_prev_layer_line, prev_layer_lines.get_lines().size())); - if ((curr_point.nearest_prev_layer_point.cast() - next_line.a).squaredNorm() < 0.1) { - quality = 0.5 * (quality + next_line.form_quality); - curling = 0.5 * (curling + next_line.curled_up_height); - } - } - - return {quality, curling}; -} - std::vector check_extrusion_entity_stability(const ExtrusionEntity *entity, const LayerRegion *layer_region, const LD &prev_layer_lines, @@ -362,7 +335,9 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit float line_len = (prev_point.position - curr_point.position).norm(); ExtrusionLine line_out{prev_point.position.cast(), curr_point.position.cast(), line_len, entity}; - auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, curr_point); + Vec2f middle = 0.5 * (line_out.a + line_out.b); + auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra(middle); + ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} : prev_layer_lines.get_line(bottom_line_idx); // correctify the distance sign using slice polygons float sign = (prev_layer_boundary.distance_from_lines(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; @@ -387,7 +362,7 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit } } else if (curr_point.distance > flow_width * 0.8f) { bridged_distance += line_len; - line_out.form_quality = prev_layer_quality - 0.3f; + line_out.form_quality = bottom_line.form_quality - 0.3f; if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) { line_out.support_point_generated = potential_cause; line_out.form_quality = 0.5f; @@ -397,8 +372,9 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit bridged_distance = 0.0f; } - line_out.curled_up_height = estimate_curled_up_height(layer_region->layer()->id() % 2 == 0 ? curr_point : prev_point, - layer_region->layer()->height, flow_width, prev_layer_curling, params); + line_out.curled_up_height = estimate_curled_up_height(middle_distance, 0.5 * (prev_point.curvature + curr_point.curvature), + layer_region->layer()->height, flow_width, bottom_line.curled_up_height, + params); lines_out.push_back(line_out); } @@ -1102,25 +1078,23 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, auto annotated_points = estimate_points_properties(pol.points, prev_layer_lines, flow_width); for (size_t i = 0; i < annotated_points.size(); ++i) { - ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; - ExtendedPoint &b = annotated_points[i]; - ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), + const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + const ExtendedPoint &b = annotated_points[i]; + ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), extrusion}; - ExtendedPoint &pivot = l->id() % 2 == 0 ? a : b; - auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, pivot); - const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? - prev_layer_lines.get_line(pivot.nearest_prev_layer_line) : - ExtrusionLine{}; + Vec2f middle = 0.5 * (line_out.a + line_out.b); + auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra(middle); + ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} : + prev_layer_lines.get_line(bottom_line_idx); - Vec2f v1 = (nearest_prev_layer_line.b - nearest_prev_layer_line.a); - Vec2f v2 = (pivot.position.cast() - nearest_prev_layer_line.a); - auto d = (v1.x() * v2.y()) - (v1.y() * v2.x()); - if (d > 0) { - pivot.distance *= -1.0f; - } + Vec2f v1 = (bottom_line.b - bottom_line.a); + Vec2f v2 = (a.position.cast() - bottom_line.a); + auto d = (v1.x() * v2.y()) - (v1.y() * v2.x()); + float sign = (d > 0) ? -1.0f : 1.0f; - line_out.curled_up_height = estimate_curled_up_height(pivot, l->height, flow_width, prev_layer_curling, params); + line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature), l->height, + flow_width, bottom_line.curled_up_height, params); current_layer_lines.push_back(line_out); } @@ -1176,22 +1150,24 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) Points extrusion_pts; extrusion->collect_points(extrusion_pts); float flow_width = get_flow_width(layer_region, extrusion->role()); - auto annotated_points = estimate_points_properties(extrusion_pts, prev_layer_lines, flow_width, - params.bridge_distance); + auto annotated_points = estimate_points_properties(extrusion_pts, prev_layer_lines, flow_width, + params.bridge_distance); for (size_t i = 0; i < annotated_points.size(); ++i) { - ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; - ExtendedPoint &b = annotated_points[i]; - ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), + const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + const ExtendedPoint &b = annotated_points[i]; + ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), extrusion}; - ExtendedPoint &pivot = l->id() % 2 == 0 ? a : b; - auto [prev_layer_quality, prev_layer_curling] = get_bottom_extrusions_quality_and_curling(prev_layer_lines, pivot); + Vec2f middle = 0.5 * (line_out.a + line_out.b); + auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra(middle); + ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} : + prev_layer_lines.get_line(bottom_line_idx); - float sign = (prev_layer_boundary.distance_from_lines(pivot.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; - pivot.distance *= sign; + // correctify the distance sign using slice polygons + float sign = (prev_layer_boundary.distance_from_lines(middle.cast()) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; - line_out.curled_up_height = estimate_curled_up_height(pivot, layer_region->layer()->height, flow_width, - prev_layer_curling, params); + line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature), + l->height, flow_width, bottom_line.curled_up_height, params); current_layer_lines.push_back(line_out); } From 3e3ccc200e703fa6b21a610a8c828be23b301873 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 13 Apr 2023 10:42:17 +0200 Subject: [PATCH 14/70] Fixed artefacts, made the new slowdown enabled with dynamic overhang speed at all times --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 35 ++++++++++------------ src/libslic3r/PrintObject.cpp | 4 ++- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index d79fee83a0..c802105635 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -265,30 +265,27 @@ public: std::vector extended_points = estimate_points_properties(path.polyline.points, prev_layer_boundaries[current_object], path.width); - for (size_t i = 0; i < int(extended_points.size()) - 1; i++) { - ExtendedPoint& ep = extended_points[i]; + std::vector processed_points; + processed_points.reserve(extended_points.size()); + for (size_t i = 0; i < extended_points.size(); i++) { + ExtendedPoint &curr = extended_points[i]; + const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; + // We are going to enforce slowdown over curled extrusions by increasing the point distance. The overhang speed is based on // signed distance from the prev layer, where 0 means fully overlapping extrusions and thus no slowdown, while extrusion_width // and more means full overhang, thus full slowdown. However, for curling, we take unsinged distance from the curled lines and // artifically modifiy the distance - Vec2d middle = 0.5 * (ep.position + extended_points[i + 1].position); - auto [distance_from_curled, line_idx, - p] = prev_curled_extrusions[current_object].distance_from_lines_extra(Point::new_scale(middle)); - if (distance_from_curled < scale_(2.0 * path.width)) { - float artificially_increased_distance = path.width * - (1.0 - (unscaled(distance_from_curled) / (2.0 * path.width)) * - (unscaled(distance_from_curled) / (2.0 * path.width))) * - (prev_curled_extrusions[current_object].get_line(line_idx).curled_height / - (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator - ep.distance = std::max(ep.distance, artificially_increased_distance); + { + Vec2d middle = 0.5 * (curr.position + next.position); + auto [distance_from_curled, line_idx, + p] = prev_curled_extrusions[current_object].distance_from_lines_extra(Point::new_scale(middle)); + if (distance_from_curled < scale_(2.5 * path.width)) { + float artificially_increased_distance = path.width * (1.0 - (unscaled(distance_from_curled) / (2.5 * path.width))) * + (prev_curled_extrusions[current_object].get_line(line_idx).curled_height / + (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator + curr.distance = std::max(curr.distance, artificially_increased_distance); + } } - } - - std::vector processed_points; - processed_points.reserve(extended_points.size()); - for (size_t i = 0; i < extended_points.size(); i++) { - const ExtendedPoint &curr = extended_points[i]; - const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; auto interpolate_speed = [](const std::map &values, float distance) { auto upper_dist = values.lower_bound(distance); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0c3bf87b98..ed3813139d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -491,7 +491,9 @@ void PrintObject::generate_support_material() void PrintObject::estimate_curled_extrusions() { if (this->set_started(posEstimateCurledExtrusions)) { - if (this->print()->config().avoid_crossing_curled_overhangs) { + if (this->print()->config().avoid_crossing_curled_overhangs || + std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(), + [](const PrintRegion *region) { return region->config().enable_dynamic_overhang_speeds.getBool(); })) { BOOST_LOG_TRIVIAL(debug) << "Estimating areas with curled extrusions - start"; m_print->set_status(88, _u8L("Estimating curled extrusions")); From f6c38fb7f9c836608ee77c25b010dd2c9ef79cc2 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 13 Apr 2023 14:03:50 +0200 Subject: [PATCH 15/70] Smoothen the results, fix some issues. --- src/libslic3r/AABBTreeLines.hpp | 6 ++-- src/libslic3r/GCode/ExtrusionProcessor.hpp | 39 ++++++++++++---------- src/libslic3r/SupportSpotsGenerator.cpp | 2 +- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index ae5569b517..990b3197fd 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -339,15 +339,15 @@ public: return {distance, nearest_line_index_out, nearest_point_out}; } - template Floating distance_from_lines(const Vec<2, typename LineType::Scalar> &point) const + template Floating distance_from_lines(const Vec<2, 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) + std::vector all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) { - return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point, radius * radius); + return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast(), radius * radius); } template std::vector, size_t>> intersections_with_line(const LineType &line) const diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index c802105635..668c89b079 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -268,22 +269,22 @@ public: std::vector processed_points; processed_points.reserve(extended_points.size()); for (size_t i = 0; i < extended_points.size(); i++) { - ExtendedPoint &curr = extended_points[i]; + const ExtendedPoint &curr = extended_points[i]; const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; - // We are going to enforce slowdown over curled extrusions by increasing the point distance. The overhang speed is based on - // signed distance from the prev layer, where 0 means fully overlapping extrusions and thus no slowdown, while extrusion_width - // and more means full overhang, thus full slowdown. However, for curling, we take unsinged distance from the curled lines and - // artifically modifiy the distance + float artificial_distance_to_curled_lines = 0.0; { - Vec2d middle = 0.5 * (curr.position + next.position); - auto [distance_from_curled, line_idx, - p] = prev_curled_extrusions[current_object].distance_from_lines_extra(Point::new_scale(middle)); - if (distance_from_curled < scale_(2.5 * path.width)) { - float artificially_increased_distance = path.width * (1.0 - (unscaled(distance_from_curled) / (2.5 * path.width))) * - (prev_curled_extrusions[current_object].get_line(line_idx).curled_height / - (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator - curr.distance = std::max(curr.distance, artificially_increased_distance); + Vec2d middle = 0.5 * (curr.position + next.position); + auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), + scale_(10.0 * path.width)); + + for (size_t idx : line_indices) { + const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx); + float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); + float dist = path.width * (1.0 - (distance_from_curled / (10.0 * path.width))) * + (1.0 - (distance_from_curled / (10.0 * path.width))) * + (line.curled_height / (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator + artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist); } } @@ -301,12 +302,14 @@ public: return (1.0f - t) * lower_dist->second + t * upper_dist->second; }; - float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), - interpolate_speed(speed_sections, next.distance)); - float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance), - interpolate_speed(fan_speed_sections, next.distance)); + float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), + interpolate_speed(speed_sections, next.distance)); + float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines); + float final_speed = std::min(curled_base_speed, extrusion_speed); + float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance), + interpolate_speed(fan_speed_sections, next.distance)); - processed_points.push_back({scaled(curr.position), extrusion_speed, int(fan_speed)}); + processed_points.push_back({scaled(curr.position), final_speed, int(fan_speed)}); } return processed_points; } diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index e9f60dabde..afc213ce70 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -212,7 +212,7 @@ float estimate_curled_up_height( float distance, float curvature, float layer_height, float flow_width, float prev_line_curled_height, Params params) { float curled_up_height = 0; - if (fabs(distance) < 2.5 * flow_width) { + if (fabs(distance) < 3.0 * flow_width) { curled_up_height = std::max(prev_line_curled_height - layer_height * 0.75f, 0.0f); } From 13c774444336b50a13ed7af6faed9f471b11f91d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 13 Apr 2023 14:37:06 +0200 Subject: [PATCH 16/70] Disable debug data output --- src/libslic3r/SupportSpotsGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index afc213ce70..e5748fad34 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -41,7 +41,7 @@ #include "Geometry/ConvexHull.hpp" // #define DETAILED_DEBUG_LOGS -#define DEBUG_FILES +// #define DEBUG_FILES #ifdef DEBUG_FILES #include From a65a68ad93c7947fd5f8818947c0e35af949b271 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 21 Apr 2023 10:09:44 +0200 Subject: [PATCH 17/70] fix missing include --- src/libslic3r/GCode/GCodeProcessor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index d0c9962900..02088ee7b1 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3,6 +3,7 @@ #include "libslic3r/Print.hpp" #include "libslic3r/LocalesUtils.hpp" #include "libslic3r/format.hpp" +#include "libslic3r/I18N.hpp" #include "libslic3r/GCodeWriter.hpp" #include "GCodeProcessor.hpp" From 8a959883b45f340dbf77fb312d210dd6a3ebbad4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Apr 2023 10:50:03 +0200 Subject: [PATCH 18/70] Hide supports when Manual editing mode is active in SLA supports gizmo --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 109490cc7d..c78a46a5bc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -123,6 +123,8 @@ void GLGizmoSlaSupports::on_render() glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); + show_sla_supports(!m_editing_mode); + render_volumes(); render_points(selection); From 29719a7ab9661602c266d1e6b22156245b6b992a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 21 Apr 2023 15:38:23 +0200 Subject: [PATCH 19/70] ClipperLib sometimes hangs forever on a single union / diff / offset task. Implemented optional time limit on ClipperLib execution. --- src/clipper/clipper.hpp | 3 +- src/libslic3r/ClipperUtils.cpp | 52 ++++++++++++++++++++++++++++ src/libslic3r/Point.hpp | 1 + src/libslic3r/Timer.cpp | 9 +++++ src/libslic3r/Timer.hpp | 63 +++++++++++++++++++++++++++++++++- 5 files changed, 126 insertions(+), 2 deletions(-) diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index 1d63616536..d190d09b5d 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -116,7 +116,8 @@ using DoublePoint = Eigen::Matrix; template using Allocator = tbb::scalable_allocator; -using Path = std::vector>; +//using Allocator = std::allocator; +using Path = std::vector>; using Paths = std::vector>; inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index d83fe4e48e..95a5337185 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -3,6 +3,20 @@ #include "ShortestPath.hpp" #include "Utils.hpp" +// #define CLIPPER_UTILS_TIMING + +#ifdef CLIPPER_UTILS_TIMING + // time limit for one ClipperLib operation (union / diff / offset), in ms + #define CLIPPER_UTILS_TIME_LIMIT_DEFAULT 50 + #include + #include "Timer.hpp" + #define CLIPPER_UTILS_TIME_LIMIT_SECONDS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000000l, BOOST_CURRENT_FUNCTION) + #define CLIPPER_UTILS_TIME_LIMIT_MILLIS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000l, BOOST_CURRENT_FUNCTION) +#else + #define CLIPPER_UTILS_TIME_LIMIT_SECONDS(limit) do {} while(false) + #define CLIPPER_UTILS_TIME_LIMIT_MILLIS(limit) do {} while(false) +#endif // CLIPPER_UTILS_TIMING + // #define CLIPPER_UTILS_DEBUG #ifdef CLIPPER_UTILS_DEBUG @@ -260,6 +274,8 @@ bool has_duplicate_points(const ClipperLib::PolyTree &polytree) template static ClipperLib::Paths raw_offset(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::ClipperOffset co; ClipperLib::Paths out; out.reserve(paths.size()); @@ -301,6 +317,8 @@ TResult clipper_do( TClip && clip, const ClipperLib::PolyFillType fillType) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Clipper clipper; clipper.AddPaths(std::forward(subject), ClipperLib::ptSubject, true); clipper.AddPaths(std::forward(clip), ClipperLib::ptClip, true); @@ -330,6 +348,8 @@ TResult clipper_union( // fillType pftNonZero and pftPositive "should" produce the same result for "normalized with implicit union" set of polygons const ClipperLib::PolyFillType fillType = ClipperLib::pftNonZero) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Clipper clipper; clipper.AddPaths(std::forward(subject), ClipperLib::ptSubject, true); TResult retval; @@ -368,6 +388,8 @@ template<> void remove_outermost_polygon(ClipperLib::PolyT template static TResult shrink_paths(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + assert(offset > 0); TResult out; if (auto raw = raw_offset(std::forward(paths), - offset, joinType, miterLimit); ! raw.empty()) { @@ -407,6 +429,8 @@ Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, C // returns number of expolygons collected (0 or 1). static int offset_expolygon_inner(const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::Paths &out) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + // 1) Offset the outer contour. ClipperLib::Paths contours; { @@ -615,6 +639,8 @@ inline ClipperLib::PolyTree clipper_do_polytree( PathProvider2 &&clip, const ClipperLib::PolyFillType fillType) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + // Perform the operation with the output to input_subject. // This pass does not generate a PolyTree, which is a very expensive operation with the current Clipper library // if there are overapping edges. @@ -753,6 +779,8 @@ Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject) template Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Clipper clipper; clipper.AddPaths(std::forward(subject), ClipperLib::ptSubject, false); clipper.AddPaths(std::forward(clip), ClipperLib::ptClip, true); @@ -938,6 +966,8 @@ Polygons union_pt_chained_outside_in(const Polygons &subject) Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths output; if (preserve_collinear) { ClipperLib::Clipper c; @@ -955,6 +985,8 @@ Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + if (! preserve_collinear) return union_ex(simplify_polygons(subject, false)); @@ -971,6 +1003,8 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear Polygons top_level_islands(const Slic3r::Polygons &polygons) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + // init Clipper ClipperLib::Clipper clipper; clipper.Clear(); @@ -994,6 +1028,8 @@ ClipperLib::Paths fix_after_outer_offset( ClipperLib::PolyFillType filltype, // = ClipperLib::pftPositive bool reverse_result) // = false { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths solution; if (! input.empty()) { ClipperLib::Clipper clipper; @@ -1012,6 +1048,8 @@ ClipperLib::Paths fix_after_inner_offset( ClipperLib::PolyFillType filltype, // = ClipperLib::pftNegative bool reverse_result) // = true { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths solution; if (! input.empty()) { ClipperLib::Clipper clipper; @@ -1032,6 +1070,8 @@ ClipperLib::Paths fix_after_inner_offset( ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + assert(contour.size() == deltas.size()); #ifndef NDEBUG @@ -1172,6 +1212,8 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v static void variable_offset_inner_raw(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + #ifndef NDEBUG // Verify that the deltas are all non positive. for (const std::vector &ds : deltas) @@ -1205,6 +1247,8 @@ static void variable_offset_inner_raw(const ExPolygon &expoly, const std::vector Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths contours, holes; variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes); @@ -1227,6 +1271,8 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths contours, holes; variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes); @@ -1253,6 +1299,8 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + #ifndef NDEBUG // Verify that the deltas are all non positive. for (const std::vector &ds : deltas) @@ -1288,6 +1336,8 @@ static void variable_offset_outer_raw(const ExPolygon &expoly, const std::vector Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths contours, holes; variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes); @@ -1309,6 +1359,8 @@ Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths contours, holes; variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes); diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index b482129cf6..c4b821ca6e 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -54,6 +54,7 @@ using Vec4d = Eigen::Matrix; template using PointsAllocator = tbb::scalable_allocator; +//using PointsAllocator = std::allocator; using Points = std::vector>; using PointPtrs = std::vector; using PointConstPtrs = std::vector; diff --git a/src/libslic3r/Timer.cpp b/src/libslic3r/Timer.cpp index b361427a65..91f3b0f09e 100644 --- a/src/libslic3r/Timer.cpp +++ b/src/libslic3r/Timer.cpp @@ -10,3 +10,12 @@ Slic3r::Timer::~Timer() BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " << duration_cast(steady_clock::now() - m_start).count() << "ms"; } + + +namespace Slic3r::Timing { + +void TimeLimitAlarm::report_time_exceeded() const { + BOOST_LOG_TRIVIAL(error) << "Time limit exceeded for " << m_limit_exceeded_message << ": " << m_timer.elapsed_seconds() << "s"; +} + +} diff --git a/src/libslic3r/Timer.hpp b/src/libslic3r/Timer.hpp index b8f9736a17..f2e5dde1a8 100644 --- a/src/libslic3r/Timer.hpp +++ b/src/libslic3r/Timer.hpp @@ -27,5 +27,66 @@ public: ~Timer(); }; +namespace Timing { + + // Timing code from Catch2 unit testing library + static inline uint64_t nanoseconds_since_epoch() { + return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + } + + // Timing code from Catch2 unit testing library + class Timer { + public: + void start() { + m_nanoseconds = nanoseconds_since_epoch(); + } + uint64_t elapsed_nanoseconds() const { + return nanoseconds_since_epoch() - m_nanoseconds; + } + uint64_t elapsed_microseconds() const { + return elapsed_nanoseconds() / 1000; + } + unsigned int elapsed_milliseconds() const { + return static_cast(elapsed_microseconds()/1000); + } + double elapsed_seconds() const { + return elapsed_microseconds() / 1000000.0; + } + private: + uint64_t m_nanoseconds = 0; + }; + + // Emits a Boost::log error if the life time of this timing object exceeds a limit. + class TimeLimitAlarm { + public: + TimeLimitAlarm(uint64_t time_limit_nanoseconds, std::string_view limit_exceeded_message) : + m_time_limit_nanoseconds(time_limit_nanoseconds), m_limit_exceeded_message(limit_exceeded_message) { + m_timer.start(); + } + ~TimeLimitAlarm() { + auto elapsed = m_timer.elapsed_nanoseconds(); + if (elapsed > m_time_limit_nanoseconds) + this->report_time_exceeded(); + } + static TimeLimitAlarm new_nanos(uint64_t time_limit_nanoseconds, std::string_view limit_exceeded_message) { + return TimeLimitAlarm(time_limit_nanoseconds, limit_exceeded_message); + } + static TimeLimitAlarm new_milis(uint64_t time_limit_milis, std::string_view limit_exceeded_message) { + return TimeLimitAlarm(uint64_t(time_limit_milis) * 1000000l, limit_exceeded_message); + } + static TimeLimitAlarm new_seconds(uint64_t time_limit_seconds, std::string_view limit_exceeded_message) { + return TimeLimitAlarm(uint64_t(time_limit_seconds) * 1000000000l, limit_exceeded_message); + } + private: + void report_time_exceeded() const; + + Timer m_timer; + uint64_t m_time_limit_nanoseconds; + std::string_view m_limit_exceeded_message; + }; + +} // namespace Catch + } // namespace Slic3r -#endif // libslic3r_Timer_hpp_ \ No newline at end of file + +#endif // libslic3r_Timer_hpp_ From 0d1522791d24dc21a96ec7c56899c5793dc3e19e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 21 Apr 2023 15:41:00 +0200 Subject: [PATCH 20/70] Refactored Arachne to use ankerl::unordered_dense hash tables instead of std::unordered_set/map The code is now generic enough to enable experiments with various hash maps in the future. --- .../Arachne/SkeletalTrapezoidation.cpp | 27 +++++++++++-------- .../Arachne/SkeletalTrapezoidation.hpp | 12 ++++++--- .../Arachne/SkeletalTrapezoidationGraph.cpp | 9 ++++--- src/libslic3r/Arachne/WallToolPaths.cpp | 5 ++-- src/libslic3r/Arachne/WallToolPaths.hpp | 6 +++-- src/libslic3r/Arachne/utils/HalfEdgeGraph.hpp | 6 +++-- .../Arachne/utils/PolylineStitcher.hpp | 1 - src/libslic3r/Arachne/utils/SparseGrid.hpp | 1 - .../Arachne/utils/SparseLineGrid.hpp | 1 - .../Arachne/utils/SparsePointGrid.hpp | 1 - src/libslic3r/Arachne/utils/SquareGrid.hpp | 1 - src/libslic3r/PerimeterGenerator.cpp | 14 +++++----- src/libslic3r/ShortEdgeCollapse.cpp | 4 ++- 13 files changed, 49 insertions(+), 39 deletions(-) diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index a73a4918a6..26d2dbeee3 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -522,9 +521,11 @@ static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph) return false; } -inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph, - const double fix_angle, - const std::unordered_map &vertex_mapping) +using PointMap = SkeletalTrapezoidation::PointMap; + +inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph, + const double fix_angle, + const PointMap &vertex_mapping) { for (STHalfEdgeNode &node : graph.nodes) { // If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction. @@ -588,7 +589,7 @@ VoronoiDiagramStatus detect_voronoi_diagram_known_issues(const Geometry::Voronoi return VoronoiDiagramStatus::NO_ISSUE_DETECTED; } -inline static std::pair, double> try_to_fix_degenerated_voronoi_diagram_by_rotation( +inline static std::pair try_to_fix_degenerated_voronoi_diagram_by_rotation( Geometry::VoronoiDiagram &voronoi_diagram, const Polygons &polys, Polygons &polys_rotated, @@ -597,7 +598,7 @@ inline static std::pair, double> try { const Polygons polys_rotated_original = polys_rotated; double fixed_by_angle = fix_angles.front(); - std::unordered_map vertex_mapping; + PointMap vertex_mapping; for (const double &fix_angle : fix_angles) { vertex_mapping.clear(); @@ -685,7 +686,7 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) const std::vector fix_angles = {PI / 6, PI / 5, PI / 7, PI / 11}; double fixed_by_angle = fix_angles.front(); - std::unordered_map vertex_mapping; + PointMap vertex_mapping; // polys_copy is referenced through items stored in the std::vector segments. Polygons polys_copy = polys; if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) { @@ -813,9 +814,11 @@ process_voronoi_diagram: edge.from->incident_edge = &edge; } +using NodeSet = SkeletalTrapezoidation::NodeSet; + void SkeletalTrapezoidation::separatePointyQuadEndNodes() { - std::unordered_set visited_nodes; + NodeSet visited_nodes; for (edge_t& edge : graph.edges) { if (edge.prev) @@ -2285,16 +2288,18 @@ void SkeletalTrapezoidation::addToolpathSegment(const ExtrusionJunction& from, c void SkeletalTrapezoidation::connectJunctions(ptr_vector_t& edge_junctions) { - std::unordered_set unprocessed_quad_starts(graph.edges.size() * 5 / 2); + using EdgeSet = ankerl::unordered_dense::set; + + EdgeSet unprocessed_quad_starts(graph.edges.size() * 5 / 2); for (edge_t& edge : graph.edges) { if (!edge.prev) { - unprocessed_quad_starts.insert(&edge); + unprocessed_quad_starts.emplace(&edge); } } - std::unordered_set passed_odd_edges; + EdgeSet passed_odd_edges; while (!unprocessed_quad_starts.empty()) { diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp index 7b8ecf8346..e2a013b154 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp @@ -7,8 +7,10 @@ #include #include // smart pointers -#include #include // pair + +#include + #include #include "utils/HalfEdgeGraph.hpp" @@ -80,7 +82,9 @@ class SkeletalTrapezoidation const BeadingStrategy& beading_strategy; public: - using Segment = PolygonsSegmentIndex; + using Segment = PolygonsSegmentIndex; + using PointMap = ankerl::unordered_dense::map; + using NodeSet = ankerl::unordered_dense::set; /*! * Construct a new trapezoidation problem to solve. @@ -164,8 +168,8 @@ protected: * mapping each voronoi VD edge to the corresponding halfedge HE edge * In case the result segment is discretized, we map the VD edge to the *last* HE edge */ - std::unordered_map vd_edge_to_he_edge; - std::unordered_map vd_node_to_he_node; + ankerl::unordered_dense::map vd_edge_to_he_edge; + ankerl::unordered_dense::map vd_node_to_he_node; node_t& makeNode(vd_t::vertex_type& vd_node, Point p); //!< Get the node which the VD node maps to, or create a new mapping if there wasn't any yet. /*! diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidationGraph.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidationGraph.cpp index 4ef96eda1a..4629396e86 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidationGraph.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidationGraph.cpp @@ -2,7 +2,8 @@ //CuraEngine is released under the terms of the AGPLv3 or higher. #include "SkeletalTrapezoidationGraph.hpp" -#include + +#include #include @@ -180,8 +181,8 @@ bool STHalfEdgeNode::isLocalMaximum(bool strict) const void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) { - std::unordered_map::iterator> edge_locator; - std::unordered_map::iterator> node_locator; + ankerl::unordered_dense::map edge_locator; + ankerl::unordered_dense::map node_locator; for (auto edge_it = edges.begin(); edge_it != edges.end(); ++edge_it) { @@ -193,7 +194,7 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) node_locator.emplace(&*node_it, node_it); } - auto safelyRemoveEdge = [this, &edge_locator](edge_t* to_be_removed, std::list::iterator& current_edge_it, bool& edge_it_is_updated) + auto safelyRemoveEdge = [this, &edge_locator](edge_t* to_be_removed, Edges::iterator& current_edge_it, bool& edge_it_is_updated) { if (current_edge_it != edges.end() && to_be_removed == &*current_edge_it) diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index 1c69fa9acd..fce69d5e4c 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -2,7 +2,6 @@ // CuraEngine is released under the terms of the AGPLv3 or higher. #include //For std::partition_copy and std::min_element. -#include #include "WallToolPaths.hpp" @@ -767,9 +766,9 @@ bool WallToolPaths::removeEmptyToolPaths(std::vector &toolpa * * \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one. */ -std::unordered_set, boost::hash>> WallToolPaths::getRegionOrder(const std::vector &input, const bool outer_to_inner) +WallToolPaths::ExtrusionLineSet WallToolPaths::getRegionOrder(const std::vector &input, const bool outer_to_inner) { - std::unordered_set, boost::hash>> order_requirements; + ExtrusionLineSet order_requirements; // We build a grid where we map toolpath vertex locations to toolpaths, // so that we can easily find which two toolpaths are next to each other, diff --git a/src/libslic3r/Arachne/WallToolPaths.hpp b/src/libslic3r/Arachne/WallToolPaths.hpp index b0bed1241a..44f3affb69 100644 --- a/src/libslic3r/Arachne/WallToolPaths.hpp +++ b/src/libslic3r/Arachne/WallToolPaths.hpp @@ -5,7 +5,8 @@ #define CURAENGINE_WALLTOOLPATHS_H #include -#include + +#include #include "BeadingStrategy/BeadingStrategyFactory.hpp" #include "utils/ExtrusionLine.hpp" @@ -73,6 +74,7 @@ public: */ static bool removeEmptyToolPaths(std::vector &toolpaths); + using ExtrusionLineSet = ankerl::unordered_dense::set, boost::hash>>; /*! * Get the order constraints of the insets when printing walls per region / hole. * Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right. @@ -81,7 +83,7 @@ public: * * \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one. */ - static std::unordered_set, boost::hash>> getRegionOrder(const std::vector &input, bool outer_to_inner); + static ExtrusionLineSet getRegionOrder(const std::vector &input, bool outer_to_inner); protected: /*! diff --git a/src/libslic3r/Arachne/utils/HalfEdgeGraph.hpp b/src/libslic3r/Arachne/utils/HalfEdgeGraph.hpp index 99efff6a07..17b06f2be2 100644 --- a/src/libslic3r/Arachne/utils/HalfEdgeGraph.hpp +++ b/src/libslic3r/Arachne/utils/HalfEdgeGraph.hpp @@ -21,8 +21,10 @@ class HalfEdgeGraph public: using edge_t = derived_edge_t; using node_t = derived_node_t; - std::list edges; - std::list nodes; + using Edges = std::list; + using Nodes = std::list; + Edges edges; + Nodes nodes; }; } // namespace Slic3r::Arachne diff --git a/src/libslic3r/Arachne/utils/PolylineStitcher.hpp b/src/libslic3r/Arachne/utils/PolylineStitcher.hpp index 2ab770a3ec..113761ce15 100644 --- a/src/libslic3r/Arachne/utils/PolylineStitcher.hpp +++ b/src/libslic3r/Arachne/utils/PolylineStitcher.hpp @@ -7,7 +7,6 @@ #include "SparsePointGrid.hpp" #include "PolygonsPointIndex.hpp" #include "../../Polygon.hpp" -#include #include namespace Slic3r::Arachne diff --git a/src/libslic3r/Arachne/utils/SparseGrid.hpp b/src/libslic3r/Arachne/utils/SparseGrid.hpp index be461d4241..45876fb9a7 100644 --- a/src/libslic3r/Arachne/utils/SparseGrid.hpp +++ b/src/libslic3r/Arachne/utils/SparseGrid.hpp @@ -6,7 +6,6 @@ #define UTILS_SPARSE_GRID_H #include -#include #include #include diff --git a/src/libslic3r/Arachne/utils/SparseLineGrid.hpp b/src/libslic3r/Arachne/utils/SparseLineGrid.hpp index a9b5368697..0b38988f92 100644 --- a/src/libslic3r/Arachne/utils/SparseLineGrid.hpp +++ b/src/libslic3r/Arachne/utils/SparseLineGrid.hpp @@ -6,7 +6,6 @@ #define UTILS_SPARSE_LINE_GRID_H #include -#include #include #include diff --git a/src/libslic3r/Arachne/utils/SparsePointGrid.hpp b/src/libslic3r/Arachne/utils/SparsePointGrid.hpp index 31c1965357..7bb51d7038 100644 --- a/src/libslic3r/Arachne/utils/SparsePointGrid.hpp +++ b/src/libslic3r/Arachne/utils/SparsePointGrid.hpp @@ -6,7 +6,6 @@ #define UTILS_SPARSE_POINT_GRID_H #include -#include #include #include "SparseGrid.hpp" diff --git a/src/libslic3r/Arachne/utils/SquareGrid.hpp b/src/libslic3r/Arachne/utils/SquareGrid.hpp index c59c3ee1b9..5787e3bf12 100644 --- a/src/libslic3r/Arachne/utils/SquareGrid.hpp +++ b/src/libslic3r/Arachne/utils/SquareGrid.hpp @@ -7,7 +7,6 @@ #include "../../Point.hpp" #include -#include #include #include diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 89add59783..1a487cba86 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -39,11 +39,11 @@ #include #include #include -#include -#include #include #include +#include + // #define ARACHNE_DEBUG #ifdef ARACHNE_DEBUG @@ -569,7 +569,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P size_t occurrence = 0; bool is_overhang = false; }; - std::unordered_map point_occurrence; + ankerl::unordered_dense::map point_occurrence; for (const ExtrusionPath &path : paths) { ++point_occurrence[path.polyline.first_point()].occurrence; ++point_occurrence[path.polyline.last_point()].occurrence; @@ -681,7 +681,7 @@ Polylines reconnect_polylines(const Polylines &polylines, double limit_distance) if (polylines.empty()) return polylines; - std::unordered_map connected; + ankerl::unordered_dense::map connected; connected.reserve(polylines.size()); for (size_t i = 0; i < polylines.size(); i++) { if (!polylines[i].empty()) { @@ -731,7 +731,7 @@ ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_f { if (extra_perims.empty()) return {}; - std::vector> dependencies(extra_perims.size()); + std::vector> dependencies(extra_perims.size()); for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) { for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) { if (paths_touch(extra_perims[path_idx], extra_perims[prev_path_idx], extrusion_spacing * 1.5f)) { @@ -1153,11 +1153,11 @@ void PerimeterGenerator::process_arachne( // Find topological order with constraints from extrusions_constrains. std::vector blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion. std::vector> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion. - std::unordered_map map_extrusion_to_idx; + ankerl::unordered_dense::map map_extrusion_to_idx; for (size_t idx = 0; idx < all_extrusions.size(); idx++) map_extrusion_to_idx.emplace(all_extrusions[idx], idx); - auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first); + Arachne::WallToolPaths::ExtrusionLineSet extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first); for (auto [before, after] : extrusions_constrains) { auto after_it = map_extrusion_to_idx.find(after); ++blocked[after_it->second]; diff --git a/src/libslic3r/ShortEdgeCollapse.cpp b/src/libslic3r/ShortEdgeCollapse.cpp index 0c940cb475..c8e4eb97e7 100644 --- a/src/libslic3r/ShortEdgeCollapse.cpp +++ b/src/libslic3r/ShortEdgeCollapse.cpp @@ -6,6 +6,8 @@ #include #include +#include + namespace Slic3r { void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count) { @@ -155,7 +157,7 @@ void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_ } //Extract the result mesh - std::unordered_map final_vertices_mapping; + ankerl::unordered_dense::map final_vertices_mapping; std::vector final_vertices; std::vector final_indices; final_indices.reserve(face_indices.size()); From b37d4e5b6c7b3f2847a18300a95c64bf1e21a989 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 Apr 2023 10:01:32 +0200 Subject: [PATCH 21/70] SPE-1674 - Follow-up of 9efed4be225615b3fac420bc2993cd0cb1e3429c - More robust fix --- src/slic3r/GUI/GLCanvas3D.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 23079b63c7..bc0fdf65cc 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -615,7 +615,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) if (m_layer_height_profile_modified) { wxGetApp().plater()->take_snapshot(_L("Variable layer height - Manual edit")); const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); - canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); wxGetApp().obj_list()->update_info_items(last_object_id); } } @@ -3189,9 +3189,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_canvas->SetFocus(); if (evt.Entering()) { - if (m_mouse.dragging && !evt.LeftIsDown() && !evt.RightIsDown() && !evt.MiddleIsDown()) - // reset dragging state if the user released the mouse button outside the 3D scene - m_mouse.dragging = false; + if (m_mouse.dragging && !evt.LeftIsDown() && !evt.RightIsDown() && !evt.MiddleIsDown()) { + // ensure to stop layers editing if enabled + if (m_layers_editing.state != LayersEditing::Unknown) { + m_layers_editing.state = LayersEditing::Unknown; + _stop_timer(); + m_layers_editing.accept_changes(*this); + } + mouse_up_cleanup(); + } //#if defined(__WXMSW__) || defined(__linux__) // // On Windows and Linux needs focus in order to catch key events From 003350a4e2a4677f95ad7a7a9c20ed2f669eead0 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 24 Apr 2023 11:22:43 +0200 Subject: [PATCH 22/70] Fix of version notification message. --- src/slic3r/GUI/GUI_App.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index dc4bb17e89..b0411c89c0 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3381,6 +3381,9 @@ void GUI_App::on_version_read(wxCommandEvent& evt) ? _u8L("Check for application update has failed.") : Slic3r::format(_u8L("You are currently running the latest released version %1%."), evt.GetString()); + if (*Semver::parse(SLIC3R_VERSION) > *Semver::parse(into_u8(evt.GetString()))) + text = Slic3r::format(_u8L("There are no new released versions online. The latest release version is %1%."), evt.GetString()); + this->plater_->get_notification_manager()->push_version_notification(NotificationType::NoNewReleaseAvailable , NotificationManager::NotificationLevel::RegularNotificationLevel , text From 9de269889cd80a66af7a4c2ddc25451f5f58b083 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 24 Apr 2023 11:34:24 +0200 Subject: [PATCH 23/70] Improved parallel_foor grain size for ensuring --- src/libslic3r/PrintObject.cpp | 53 ++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c6329f2e4e..46588b9b02 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -56,6 +56,20 @@ using namespace std::literals; +// #define PRINT_OBJECT_TIMING + +#ifdef PRINT_OBJECT_TIMING + // time limit for one ClipperLib operation (union / diff / offset), in ms + #define PRINT_OBJECT_TIME_LIMIT_DEFAULT 50 + #include + #include "Timer.hpp" + #define PRINT_OBJECT_TIME_LIMIT_SECONDS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000000l, BOOST_CURRENT_FUNCTION) + #define PRINT_OBJECT_TIME_LIMIT_MILLIS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000l, BOOST_CURRENT_FUNCTION) +#else + #define PRINT_OBJECT_TIME_LIMIT_SECONDS(limit) do {} while(false) + #define PRINT_OBJECT_TIME_LIMIT_MILLIS(limit) do {} while(false) +#endif // PRINT_OBJECT_TIMING + #ifdef SLIC3R_DEBUG_SLICE_PROCESSING #define SLIC3R_DEBUG #endif @@ -178,6 +192,7 @@ void PrintObject::make_perimeters() tbb::parallel_for( tbb::blocked_range(0, m_layers.size() - 1), [this, ®ion, region_id](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id); @@ -237,6 +252,7 @@ void PrintObject::make_perimeters() tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); m_layers[layer_idx]->make_perimeters(); @@ -408,6 +424,7 @@ void PrintObject::infill() tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get()); @@ -431,6 +448,7 @@ void PrintObject::ironing() // Ironing starting with layer 0 to support ironing all surfaces. tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); m_layers[layer_idx]->make_ironing(); @@ -530,16 +548,17 @@ std::pair PrintObject::prepare std::vector> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1))); // ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally tbb::parallel_for(tbb::blocked_range(0, surfaces_w_bottom_z.size()), - [this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range &range) { - for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) { - std::vector &out = overhangs[surface_idx]; - m_print->throw_if_canceled(); - append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon, - surfaces_w_bottom_z[surface_idx].second)); - for (Vec3d &p : out) - p = (to_octree * p).eval(); - } - }); + [this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range &range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); + for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) { + std::vector &out = overhangs[surface_idx]; + m_print->throw_if_canceled(); + append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon, + surfaces_w_bottom_z[surface_idx].second)); + for (Vec3d &p : out) + p = (to_octree * p).eval(); + } + }); // and gather them. for (size_t i = 1; i < overhangs.size(); ++ i) append(overhangs.front(), std::move(overhangs[i])); @@ -911,6 +930,7 @@ void PrintObject::detect_surfaces_type() // In non-spiral vase mode, go over all layers. m_layers.size()), [this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating // the support from the print. SurfaceType surface_type_bottom_other = @@ -1059,6 +1079,7 @@ void PrintObject::detect_surfaces_type() tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this, region_id](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { m_print->throw_if_canceled(); LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id]; @@ -1117,6 +1138,7 @@ void PrintObject::process_external_surfaces() tbb::parallel_for( tbb::blocked_range(0, m_layers.size() - 1), [this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) if (layer_expansions_and_voids[layer_idx + 1]) { // Layer above is partially filled with solid infill (top, bottom, bridging...), @@ -1142,6 +1164,7 @@ void PrintObject::process_external_surfaces() tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this, &surfaces_covered, region_id](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z; @@ -1194,6 +1217,7 @@ void PrintObject::discover_vertical_shells() tbb::parallel_for( tbb::blocked_range(0, num_layers, grain_size), [this, &cache_top_botom_regions](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); const std::initializer_list surfaces_bottom { stBottom, stBottomBridge }; const size_t num_regions = this->num_printing_regions(); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { @@ -1267,6 +1291,7 @@ void PrintObject::discover_vertical_shells() tbb::parallel_for( tbb::blocked_range(0, num_layers, grain_size), [this, region_id, &cache_top_botom_regions](const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); const std::initializer_list surfaces_bottom { stBottom, stBottomBridge }; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { m_print->throw_if_canceled(); @@ -1292,10 +1317,12 @@ void PrintObject::discover_vertical_shells() } BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness"; + grain_size = 1; tbb::parallel_for( tbb::blocked_range(0, num_layers, grain_size), [this, region_id, &cache_top_botom_regions] (const tbb::blocked_range& range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { m_print->throw_if_canceled(); @@ -1626,6 +1653,7 @@ void PrintObject::bridge_over_infill() tbb::concurrent_vector candidate_surfaces; tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), &candidate_surfaces](tbb::blocked_range r) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { const Layer *layer = po->get_layer(lidx); if (layer->lower_layer == nullptr) { @@ -1723,6 +1751,7 @@ void PrintObject::bridge_over_infill() tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), &layers_to_generate_infill, &infill_lines](tbb::blocked_range r) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { size_t lidx = layers_to_generate_infill[job_idx]; infill_lines.at( @@ -1754,6 +1783,7 @@ void PrintObject::bridge_over_infill() tbb::parallel_for(tbb::blocked_range(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, &layer_area_covered_by_candidates]( tbb::blocked_range r) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { size_t lidx = layers_with_candidates[job_idx]; for (const auto &candidate : surfaces_by_layer.at(lidx)) { @@ -2072,6 +2102,7 @@ void PrintObject::bridge_over_infill() determine_bridging_angle, construct_anchored_polygon]( tbb::blocked_range r) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) { for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) { size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx]; @@ -2244,6 +2275,7 @@ void PrintObject::bridge_over_infill() BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info(); tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range r) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) continue; @@ -2760,6 +2792,7 @@ static void project_triangles_to_slabs(SpanOfConstPtrs layers, const inde [&custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range& range) { for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); std::array facet; // Transform the triangle into worlds coords. From bbea397aa662a01e8b42ea13159aa4109a615573 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Apr 2023 10:02:50 +0200 Subject: [PATCH 24/70] SPE-1675 - Do not update layer editing when the mouse cursor is moved outside of the 3D scene while dragging --- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index bc0fdf65cc..943f08a637 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6244,10 +6244,10 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) m_layers_editing.last_action = evt->ShiftDown() ? (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_SMOOTH : LAYER_HEIGHT_EDIT_ACTION_REDUCE) : (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_INCREASE : LAYER_HEIGHT_EDIT_ACTION_DECREASE); - } - m_layers_editing.adjust_layer_height_profile(); - _refresh_if_shown_on_screen(); + m_layers_editing.adjust_layer_height_profile(); + _refresh_if_shown_on_screen(); + } // Automatic action on mouse down with the same coordinate. _start_timer(); From f9c1abbd50635dadb0b423bbc07418a85ea471d7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 Apr 2023 13:58:01 +0200 Subject: [PATCH 25/70] SPE-1677 - Disable SLA supports and Hollow gizmos when the selected object is non-printable. Do not allow to set to non-printable an object while the SLA supports and Hollow gizmos are active. --- src/slic3r/GUI/GUI_Factories.cpp | 8 ++++++++ src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 6 ++++++ src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 14 ++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 7 ++++--- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 4c12fbe23d..045bd146a1 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -743,6 +743,14 @@ wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu) } evt.Check(check); + + // disable the menu item if SLA supports or Hollow gizmos are active + if (printer_technology() == ptSLA) { + const auto gizmo_type = plater()->canvas3D()->get_gizmos_manager().get_current_type(); + const bool enable = gizmo_type != GLGizmosManager::SlaSupports && gizmo_type != GLGizmosManager::Hollow; + evt.Enable(enable); + } + plater()->set_current_canvas_as_dirty(); }, menu_item_printable->GetId()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 91b2bd8795..0a4217387e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -820,6 +820,12 @@ bool GLGizmoHollow::on_is_activable() const if (selection.get_volume(idx)->is_outside && selection.get_volume(idx)->composite_id.volume_id >= 0) return false; + // Check that none of the selected volumes is marked as non-pritable. + for (const auto& idx : list) { + if (!selection.get_volume(idx)->printable) + return false; + } + return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index c78a46a5bc..b9ec5cf19b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -65,9 +65,13 @@ void GLGizmoSlaSupports::data_changed(bool is_serializing) // If we triggered autogeneration before, check backend and fetch results if they are there if (mo) { m_c->instances_hider()->set_hide_full_scene(true); - const SLAPrintObject* po = m_c->selection_info()->print_object(); + + int last_comp_step = slaposCount; const int required_step = get_min_sla_print_object_step(); - auto last_comp_step = static_cast(po->last_completed_step()); + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po != nullptr) + last_comp_step = static_cast(po->last_completed_step()); + if (last_comp_step == slaposCount) last_comp_step = -1; @@ -793,6 +797,12 @@ bool GLGizmoSlaSupports::on_is_activable() const if (selection.get_volume(idx)->is_outside && selection.get_volume(idx)->composite_id.volume_id >= 0) return false; + // Check that none of the selected volumes is marked as non-pritable. + for (const auto& idx : list) { + if (!selection.get_volume(idx)->printable) + return false; + } + return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index debb225358..4edb01c2bd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -261,11 +261,12 @@ void Raycaster::on_update() // For sla printers we use the mesh generated by the backend std::shared_ptr preview_mesh_ptr; const SLAPrintObject* po = get_pool()->selection_info()->print_object(); - if (po) + if (po != nullptr) preview_mesh_ptr = po->get_mesh_to_print(); + else + preview_mesh_ptr.reset(); - if (preview_mesh_ptr) - m_sla_mesh_cache = TriangleMesh{*preview_mesh_ptr}; + m_sla_mesh_cache = (preview_mesh_ptr != nullptr) ? TriangleMesh{ *preview_mesh_ptr } : TriangleMesh(); if (!m_sla_mesh_cache.empty()) { m_sla_mesh_cache.transform(po->trafo().inverse()); From f1c5ffddfa780b868573ff803a79811e431a6def Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 26 Apr 2023 09:06:17 +0200 Subject: [PATCH 26/70] Fixed typo --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 943f08a637..dbb2106802 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6274,7 +6274,7 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) { const Linef3 ray = mouse_ray(mouse_pos); - return (std::abs(ray.unit_vector().z() < EPSILON)) ? ray.a : ray.intersect_plane(0.0); + return (std::abs(ray.unit_vector().z()) < EPSILON) ? ray.a : ray.intersect_plane(0.0); } void GLCanvas3D::_start_timer() From 4986afe94fc725e07b2410022d82486b7d3f8720 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 26 Apr 2023 09:34:02 +0200 Subject: [PATCH 27/70] Tech ENABLE_GCODE_POSTPROCESS_BACKTRACE set as default --- src/libslic3r/GCode/GCodeProcessor.cpp | 144 ------------------------- src/libslic3r/GCode/GCodeProcessor.hpp | 2 - src/libslic3r/GCodeReader.hpp | 2 - src/libslic3r/Technologies.hpp | 2 - 4 files changed, 150 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 02088ee7b1..9dab740e5b 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -442,9 +442,7 @@ void GCodeProcessorResult::reset() { max_print_height = 0.0f; settings_ids.reset(); extruders_count = 0; -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE backtrace_enabled = false; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE extruder_colors = std::vector(); filament_diameters = std::vector(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_densities = std::vector(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); @@ -462,9 +460,7 @@ void GCodeProcessorResult::reset() { max_print_height = 0.0f; settings_ids.reset(); extruders_count = 0; -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE backtrace_enabled = false; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE extruder_colors = std::vector(); filament_diameters = std::vector(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_densities = std::vector(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); @@ -558,9 +554,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_producer = EProducer::PrusaSlicer; m_flavor = config.gcode_flavor; -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE m_result.backtrace_enabled = is_XL_printer(config); -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE size_t extruders_count = config.nozzle_diameter.values.size(); m_result.extruders_count = extruders_count; @@ -3477,7 +3471,6 @@ void GCodeProcessor::post_process() last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time); } -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE // Helper class to modify and export gcode to file class ExportLines { @@ -3712,26 +3705,15 @@ void GCodeProcessor::post_process() }; ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); -#else - // buffer line to export only when greater than 64K to reduce writing calls - std::string export_line; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE // replace placeholder lines with the proper final value // gcode_line is in/out parameter, to reduce expensive memory allocation auto process_placeholders = [&](std::string& gcode_line) { -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE bool processed = false; -#else - unsigned int extra_lines_count = 0; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE // remove trailing '\n' auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1); -#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE - std::string ret; -#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE if (line.length() > 1) { line = line.substr(1); if (m_time_processor.export_remaining_time_enabled && @@ -3740,29 +3722,16 @@ void GCodeProcessor::post_process() const TimeMachine& machine = m_time_processor.machines[i]; if (machine.enabled) { // export pair -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(), (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100, (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0)); processed = true; -#else - ret += format_line_M73_main(machine.line_m73_main_mask.c_str(), - (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100, - (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0); - ++extra_lines_count; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE // export remaining time to next printer stop if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) { const int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time); -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); last_exported_stop[i] = to_export_stop; -#else - ret += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop); - last_exported_stop[i] = to_export_stop; - ++extra_lines_count; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE } } } @@ -3776,12 +3745,8 @@ void GCodeProcessor::post_process() sprintf(buf, "; estimated printing time (%s mode) = %s\n", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", get_time_dhms(machine.time).c_str()); -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE export_lines.append_line(buf); processed = true; -#else - ret += buf; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE } } for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { @@ -3792,25 +3757,14 @@ void GCodeProcessor::post_process() sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str()); -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE export_lines.append_line(buf); processed = true; -#else - ret += buf; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE } } } } -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE return processed; -#else - if (!ret.empty()) - // Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed. - gcode_line = ret; - return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1); -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE }; std::vector filament_mm(m_result.extruders_count, 0.0); @@ -3884,16 +3838,8 @@ void GCodeProcessor::post_process() time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute, // Caches, to be modified &g1_times_cache_it, &last_exported_main, &last_exported_stop, -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE &export_lines] -#else - // String output - &export_line] -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE (const size_t g1_lines_counter) { -#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE - unsigned int exported_lines_count = 0; -#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE if (m_time_processor.export_remaining_time_enabled) { for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = m_time_processor.machines[i]; @@ -3907,17 +3853,9 @@ void GCodeProcessor::post_process() std::pair to_export_main = { int(100.0f * it->elapsed_time / machine.time), time_in_minutes(machine.time - it->elapsed_time) }; if (last_exported_main[i] != to_export_main) { -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(), to_export_main.first, to_export_main.second)); -#else - export_line += format_line_M73_main(machine.line_m73_main_mask.c_str(), - to_export_main.first, to_export_main.second); -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE last_exported_main[i] = to_export_main; -#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE - ++exported_lines_count; -#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE } // export remaining time to next printer stop auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time, @@ -3927,15 +3865,8 @@ void GCodeProcessor::post_process() if (last_exported_stop[i] != to_export_stop) { if (to_export_stop > 0) { if (last_exported_stop[i] != to_export_stop) { -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); -#else - export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop); -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE last_exported_stop[i] = to_export_stop; -#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE - ++exported_lines_count; -#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE } } else { @@ -3954,22 +3885,12 @@ void GCodeProcessor::post_process() } if (is_last) { -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE if (std::distance(machine.stop_times.begin(), it_stop) == static_cast(machine.stop_times.size() - 1)) export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); else export_lines.append_line(format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time))); -#else - if (std::distance(machine.stop_times.begin(), it_stop) == static_cast(machine.stop_times.size() - 1)) - export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop); - else - export_line += format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time)); -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE last_exported_stop[i] = to_export_stop; -#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE - ++exported_lines_count; -#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE } } } @@ -3978,12 +3899,8 @@ void GCodeProcessor::post_process() } } } -#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE - return exported_lines_count; -#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE }; -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE // add lines XXX to exported gcode auto process_line_T = [this, &export_lines](const std::string& gcode_line, const size_t g1_lines_counter, const ExportLines::Backtrace& backtrace) { const std::string cmd = GCodeReader::GCodeLine::extract_cmd(gcode_line); @@ -4028,37 +3945,15 @@ void GCodeProcessor::post_process() }); } }; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE m_result.lines_ends.clear(); -#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE - // helper function to write to disk - size_t out_file_pos = 0; - auto write_string = [this, &export_line, &out, &out_path, &out_file_pos](const std::string& str) { - fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f); - if (ferror(out.f)) { - out.close(); - boost::nowide::remove(out_path.c_str()); - throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n")); - } - for (size_t i = 0; i < export_line.size(); ++i) - if (export_line[i] == '\n') - m_result.lines_ends.emplace_back(out_file_pos + i + 1); - out_file_pos += export_line.size(); - export_line.clear(); - }; -#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE unsigned int line_id = 0; -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE // Backtrace data for Tx gcode lines static const ExportLines::Backtrace backtrace_T = { 120.0f, 10 }; // In case there are multiple sources of backtracing, keeps track of the longest backtrack time needed // to flush the backtrace cache accordingly float max_backtrace_time = 120.0f; -#else - std::vector> offsets; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE { // Read the input stream 64kB at a time, extract lines and process them. @@ -4082,24 +3977,15 @@ void GCodeProcessor::post_process() gcode_line.insert(gcode_line.end(), it, it_end); if (eol) { ++line_id; -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE export_lines.update(line_id, g1_lines_counter); -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE gcode_line += "\n"; // replace placeholder lines -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE bool processed = process_placeholders(gcode_line); if (processed) gcode_line.clear(); -#else - auto [processed, lines_added_count] = process_placeholders(gcode_line); - if (processed && lines_added_count > 0) - offsets.push_back({ line_id, lines_added_count }); -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE if (!processed) processed = process_used_filament(gcode_line); -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE if (!processed && !is_temporary_decoration(gcode_line)) { if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) // add lines M73 where needed @@ -4114,18 +4000,6 @@ void GCodeProcessor::post_process() if (!gcode_line.empty()) export_lines.append_line(gcode_line); export_lines.write(out, 1.1f * max_backtrace_time, m_result, out_path); -#else - if (!processed && !is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) { - // remove temporary lines, add lines M73 where needed - unsigned int extra_lines_count = process_line_G1(g1_lines_counter++); - if (extra_lines_count > 0) - offsets.push_back({ line_id, extra_lines_count }); - } - - export_line += gcode_line; - if (export_line.length() > 65535) - write_string(export_line); -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE gcode_line.clear(); } // Skip EOL. @@ -4140,30 +4014,12 @@ void GCodeProcessor::post_process() } } -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE export_lines.flush(out, m_result, out_path); -#else - if (!export_line.empty()) - write_string(export_line); -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE out.close(); in.close(); -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE export_lines.synchronize_moves(m_result); -#else - // updates moves' gcode ids which have been modified by the insertion of the M73 lines - unsigned int curr_offset_id = 0; - unsigned int total_offset = 0; - for (GCodeProcessorResult::MoveVertex& move : m_result.moves) { - while (curr_offset_id < static_cast(offsets.size()) && offsets[curr_offset_id].first <= move.gcode_id) { - total_offset += offsets[curr_offset_id].second; - ++curr_offset_id; - } - move.gcode_id += total_offset; - } -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE if (rename_file(out_path, m_result.filename)) throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' + diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 1f29488c38..26cb898943 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -127,9 +127,7 @@ namespace Slic3r { float max_print_height; SettingsIds settings_ids; size_t extruders_count; -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE bool backtrace_enabled; -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE std::vector extruder_colors; std::vector filament_diameters; std::vector filament_densities; diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index bf85a5131f..58f55fdcf7 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -71,7 +71,6 @@ public: return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]); } -#if ENABLE_GCODE_POSTPROCESS_BACKTRACE static bool cmd_starts_with(const std::string& gcode_line, const char* cmd_test) { return strncmp(GCodeReader::skip_whitespaces(gcode_line.c_str()), cmd_test, strlen(cmd_test)) == 0; } @@ -82,7 +81,6 @@ public: const std::string_view cmd = temp.cmd(); return { cmd.begin(), cmd.end() }; } -#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE private: std::string m_raw; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index c33f24311a..60d89c9e9c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,8 +58,6 @@ // Enable alternative version of file_wildcards() #define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1) -// Enable gcode postprocess modified to allow for backward insertion of new lines -#define ENABLE_GCODE_POSTPROCESS_BACKTRACE (1 && ENABLE_2_6_0_ALPHA1) #endif // _prusaslicer_technologies_h_ From e3a868202d3e5aa6c843bb579886fab3c0e60c4a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 26 Apr 2023 09:57:21 +0200 Subject: [PATCH 28/70] Fix for SPE-1681 - Sidebar: Text in support field can be deleted --- src/slic3r/GUI/Plater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6adabb7d68..5657f30f2d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -437,7 +437,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : support_def.label = L("Supports"); support_def.type = coStrings; support_def.tooltip = L("Select what kind of support do you need"); - support_def.set_enum_labels(ConfigOptionDef::GUIType::select_open, { + support_def.set_enum_labels(ConfigOptionDef::GUIType::select_close, { L("None"), L("Support on build plate only"), L("For support enforcers only"), @@ -592,7 +592,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : pad_def.label = L("Pad"); pad_def.type = coStrings; pad_def.tooltip = L("Select what kind of pad do you need"); - pad_def.set_enum_labels(ConfigOptionDef::GUIType::select_open, { + pad_def.set_enum_labels(ConfigOptionDef::GUIType::select_close, { L("None"), L("Below object"), L("Around object") From cde530901ae5a6434206c8b3882d5618f8de9afe Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 26 Apr 2023 10:26:27 +0200 Subject: [PATCH 29/70] Follow up e3a868202d3e5aa6c843bb579886fab3c0e60c4a - fixed assert --- src/libslic3r/Config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 538580d506..28410b87dc 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1991,7 +1991,7 @@ public: void set_enum_labels(GUIType gui_type, const std::initializer_list il) { this->enum_def_new(); - assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_open); + assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_close); this->gui_type = gui_type; enum_def->set_labels(il); } From cdf8cd83d5aa69c71e378c045a9037e77a16bb3d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 26 Apr 2023 12:31:59 +0200 Subject: [PATCH 30/70] Follow-up of 6e7fefbabf067cfe1a54b638e16e494218432276 - Force using glGeneratedMipmap() function on AMD Custom cards, no matter what's the installed driver (Windows only) --- src/slic3r/GUI/OpenGLManager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 447da442ef..f0713932db 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -419,9 +419,12 @@ bool OpenGLManager::init_gl() // There is no an easy way to detect the driver version without using Win32 API because the strings returned by OpenGL // have no standardized format, only some of them contain the driver version. // Until we do not know that driver will be fixed (if ever) we force the use of power of two textures on all cards - // containing the string 'Radeon' in the string returned by glGetString(GL_RENDERER) + // 1) containing the string 'Radeon' in the string returned by glGetString(GL_RENDERER) + // 2) containing the string 'Custom' in the string returned by glGetString(GL_RENDERER) const auto& gl_info = OpenGLManager::get_gl_info(); - if (boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.") && boost::contains(gl_info.get_renderer(), "Radeon")) + if (boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.") && + (boost::contains(gl_info.get_renderer(), "Radeon") || + boost::contains(gl_info.get_renderer(), "Custom"))) s_force_power_of_two_textures = true; #endif // _WIN32 } From 4c872b03528b292975bb60729076cbd6efb1054a Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 27 Apr 2023 15:36:21 +0200 Subject: [PATCH 31/70] Paralellize SupportSpotGenerator! Fix extra perimeters crash - problem with new ankerl hash map Fix progress bar --- src/libslic3r/PerimeterGenerator.cpp | 5 +- src/libslic3r/Print.cpp | 1 - src/libslic3r/SupportSpotsGenerator.cpp | 289 +++++++++++++----------- 3 files changed, 162 insertions(+), 133 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 1a487cba86..e1068d7636 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -681,7 +682,7 @@ Polylines reconnect_polylines(const Polylines &polylines, double limit_distance) if (polylines.empty()) return polylines; - ankerl::unordered_dense::map connected; + std::unordered_map connected; connected.reserve(polylines.size()); for (size_t i = 0; i < polylines.size(); i++) { if (!polylines[i].empty()) { @@ -731,7 +732,7 @@ ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_f { if (extra_perims.empty()) return {}; - std::vector> dependencies(extra_perims.size()); + std::vector> dependencies(extra_perims.size()); for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) { for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) { if (paths_touch(extra_perims[path_idx], extra_perims[prev_path_idx], extrusion_spacing * 1.5f)) { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 49d17e5c0b..cf8b5c577d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -880,7 +880,6 @@ void Print::process() BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info(); for (PrintObject *obj : m_objects) obj->make_perimeters(); - this->set_status(70, _u8L("Infilling layers")); for (PrintObject *obj : m_objects) obj->infill(); for (PrintObject *obj : m_objects) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index e5748fad34..4c13cdd484 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -172,6 +174,69 @@ struct SliceConnection } }; +SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer) +{ + SliceConnection connection; + + const LayerSlice &slice = layer->lslices_ex[slice_idx]; + Polygons slice_polys = to_polygons(layer->lslices[slice_idx]); + BoundingBox slice_bb = get_extents(slice_polys); + const Layer *lower_layer = layer->lower_layer; + + ExPolygons below{}; + for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); } + Polygons below_polys = to_polygons(below); + + BoundingBox below_bb = get_extents(below_polys); + + Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb), + ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb)); + + for (const Polygon &poly : overlap) { + Vec2f p0 = unscaled(poly.first_point()).cast(); + for (size_t i = 2; i < poly.points.size(); i++) { + Vec2f p1 = unscaled(poly.points[i - 1]).cast(); + Vec2f p2 = unscaled(poly.points[i]).cast(); + + float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f; + + auto [area, first_moment_of_area, second_moment_area, + second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2); + connection.area += sign * area; + connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area); + connection.second_moment_of_area_accumulator += sign * second_moment_area; + connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance; + } + } + + return connection; +}; + +using PrecomputedSliceConnections = std::vector>; +PrecomputedSliceConnections precompute_slices_connections(const PrintObject *po) +{ + PrecomputedSliceConnections result{}; + for (size_t lidx = 0; lidx < po->layer_count(); lidx++) { + result.emplace_back(std::vector{}); + for (size_t slice_idx = 0; slice_idx < po->get_layer(lidx)->lslices_ex.size(); slice_idx++) { + result[lidx].push_back(SliceConnection{}); + } + } + + tbb::parallel_for(tbb::blocked_range(0, po->layers().size()), [po, &result](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + const Layer *l = po->get_layer(lidx); + tbb::parallel_for(tbb::blocked_range(0, l->lslices_ex.size()), [lidx, l, &result](tbb::blocked_range r2) { + for (size_t slice_idx = r2.begin(); slice_idx < r2.end(); slice_idx++) { + result[lidx][slice_idx] = estimate_slice_connection(slice_idx, l); + } + }); + } + }); + + return result; +}; + float get_flow_width(const LayerRegion *region, ExtrusionRole role) { if (role == ExtrusionRole::BridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width(); @@ -253,15 +318,8 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit const AABBTreeLines::LinesDistancer &prev_layer_boundary, const Params ¶ms) { - if (entity->is_collection()) { - std::vector checked_lines_out; - checked_lines_out.reserve(prev_layer_lines.get_lines().size() / 3); - for (const auto *e : static_cast(entity)->entities) { - auto tmp = check_extrusion_entity_stability(e, layer_region, prev_layer_lines, prev_layer_boundary, params); - checked_lines_out.insert(checked_lines_out.end(), tmp.begin(), tmp.end()); - } - return checked_lines_out; - } else if (entity->role().is_bridge() && !entity->role().is_perimeter()) { + assert(!entity->is_collection()); + if (entity->role().is_bridge() && !entity->role().is_perimeter()) { // pure bridges are handled separately, beacuse we need to align the forward and backward direction support points if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) { return {}; @@ -344,9 +402,10 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit curr_point.distance *= sign; SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion; - if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) { - potential_cause = SupportPointCause::FloatingExtrusion; - } + // Bridges are now separated. While long overhang perimeter is technically bridge, it would confuse the users + // if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) { + // potential_cause = SupportPointCause::FloatingExtrusion; + // } float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f, params.bridge_distance / @@ -383,44 +442,6 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit } } -SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer) -{ - SliceConnection connection; - - const LayerSlice &slice = layer->lslices_ex[slice_idx]; - Polygons slice_polys = to_polygons(layer->lslices[slice_idx]); - BoundingBox slice_bb = get_extents(slice_polys); - const Layer *lower_layer = layer->lower_layer; - - ExPolygons below{}; - for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); } - Polygons below_polys = to_polygons(below); - - BoundingBox below_bb = get_extents(below_polys); - - Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb), - ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb)); - - for (const Polygon &poly : overlap) { - Vec2f p0 = unscaled(poly.first_point()).cast(); - for (size_t i = 2; i < poly.points.size(); i++) { - Vec2f p1 = unscaled(poly.points[i - 1]).cast(); - Vec2f p2 = unscaled(poly.points[i]).cast(); - - float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f; - - auto [area, first_moment_of_area, second_moment_area, - second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2); - connection.area += sign * area; - connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area); - connection.second_moment_of_area_accumulator += sign * second_moment_area; - connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance; - } - } - - return connection; -}; - class ObjectPart { public: @@ -761,7 +782,10 @@ public: } }; -std::tuple check_stability(const PrintObject *po, const PrintTryCancel &cancel_func, const Params ¶ms) +std::tuple check_stability(const PrintObject *po, + const PrecomputedSliceConnections &precomputed_slices_connections, + const PrintTryCancel &cancel_func, + const Params ¶ms) { SupportPoints supp_points{}; SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points); @@ -790,8 +814,8 @@ std::tuple check_stability(const PrintObject *po, for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { const LayerSlice &slice = layer->lslices_ex.at(slice_idx); - auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params); - SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer); + auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params); + const SliceConnection &connection_to_below = precomputed_slices_connections[layer_idx][slice_idx]; #ifdef DETAILED_DEBUG_LOGS std::cout << "SLICE IDX: " << slice_idx << std::endl; @@ -858,25 +882,87 @@ std::tuple check_stability(const PrintObject *po, prev_slice_idx_to_weakest_connection = next_slice_idx_to_weakest_connection; next_slice_idx_to_weakest_connection.clear(); + auto get_flat_entities = [](const ExtrusionEntity *e) { + std::vector entities; + std::vector queue{e}; + while (!queue.empty()) { + const ExtrusionEntity *next = queue.back(); + queue.pop_back(); + if (next->is_collection()) { + for (const ExtrusionEntity *e : static_cast(next)->entities) { + queue.push_back(e); + } + } else { + entities.push_back(next); + } + } + return entities; + }; + + struct EnitityToCheck + { + const ExtrusionEntity *e; + const LayerRegion *region; + size_t slice_idx; + }; + std::vector entities_to_check; + for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { + const LayerSlice &slice = layer->lslices_ex.at(slice_idx); + for (const auto &island : slice.islands) { + for (const LayerExtrusionRange &fill_range : island.fills) { + const LayerRegion *fill_region = layer->get_region(fill_range.region()); + for (const auto &fill_idx : fill_range) { + for (const ExtrusionEntity *e : get_flat_entities(fill_region->fills().entities[fill_idx])) { + if (e->role() == ExtrusionRole::BridgeInfill) { + entities_to_check.push_back({e, fill_region, slice_idx}); + } + } + } + } + + const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region()); + for (const size_t &perimeter_idx : island.perimeters) { + for (const ExtrusionEntity *e : get_flat_entities(perimeter_region->perimeters().entities[perimeter_idx])) { + entities_to_check.push_back({e, perimeter_region, slice_idx}); + } + } + } + } + + AABBTreeLines::LinesDistancer prev_layer_boundary = layer->lower_layer != nullptr ? + AABBTreeLines::LinesDistancer{ + to_unscaled_linesf(layer->lower_layer->lslices)} : + AABBTreeLines::LinesDistancer{}; + + std::vector> unstable_lines_per_slice(layer->lslices_ex.size()); + std::vector> ext_perim_lines_per_slice(layer->lslices_ex.size()); + + tbb::parallel_for(tbb::blocked_range(0, entities_to_check.size()), + [&entities_to_check, &prev_layer_ext_perim_lines, &prev_layer_boundary, &unstable_lines_per_slice, + &ext_perim_lines_per_slice, ¶ms](tbb::blocked_range r) { + for (size_t entity_idx = r.begin(); entity_idx < r.end(); ++entity_idx) { + const auto &e_to_check = entities_to_check[entity_idx]; + for (const auto &line : + check_extrusion_entity_stability(e_to_check.e, e_to_check.region, prev_layer_ext_perim_lines, + prev_layer_boundary, params)) { + if (line.support_point_generated.has_value()) { + unstable_lines_per_slice[e_to_check.slice_idx].push_back(line); + } + if (line.is_external_perimeter()) { + ext_perim_lines_per_slice[e_to_check.slice_idx].push_back(line); + } + } + } + }); + std::vector current_layer_ext_perims_lines{}; current_layer_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size()); // All object parts updated, and for each slice we have coresponding weakest connection. // We can now check each slice and its corresponding weakest connection and object part for stability. for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { - const LayerSlice &slice = layer->lslices_ex.at(slice_idx); ObjectPart &part = active_object_parts.access(prev_slice_idx_to_object_part_mapping[slice_idx]); SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx]; - std::vector boundary_lines; - for (const auto &link : slice.overlaps_below) { - auto ls = to_unscaled_linesf({layer->lower_layer->lslices[link.slice_idx]}); - boundary_lines.insert(boundary_lines.end(), ls.begin(), ls.end()); - } - AABBTreeLines::LinesDistancer prev_layer_boundary{std::move(boundary_lines)}; - - - std::vector current_slice_ext_perims_lines{}; - current_slice_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size() / layer->lslices_ex.size()); #ifdef DETAILED_DEBUG_LOGS weakest_conn.print_info("weakest connection info: "); #endif @@ -911,73 +997,15 @@ std::tuple check_stability(const PrintObject *po, } }; - // first we will check local extrusion stability of bridges, then of perimeters. Perimeters are more important, they - // account for most of the curling and possible crashes, so on them we will run also global stability check - for (const auto &island : slice.islands) { - // Support bridges where needed. - for (const LayerExtrusionRange &fill_range : island.fills) { - const LayerRegion *fill_region = layer->get_region(fill_range.region()); - for (const auto &fill_idx : fill_range) { - const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx]; - if (entity->role() == ExtrusionRole::BridgeInfill) { - for (const ExtrusionLine &bridge : - check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines, prev_layer_boundary, - params)) { - if (bridge.support_point_generated.has_value()) { - reckon_new_support_point(*bridge.support_point_generated, create_support_point_position(bridge.b), - float(-EPSILON), Vec2f::Zero()); - } - } - } - } - } - - const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region()); - for (const auto &perimeter_idx : island.perimeters) { - const ExtrusionEntity *entity = perimeter_region->perimeters().entities[perimeter_idx]; - std::vector perims = check_extrusion_entity_stability(entity, perimeter_region, - prev_layer_ext_perim_lines, prev_layer_boundary, - params); - for (const ExtrusionLine &perim : perims) { - if (perim.support_point_generated.has_value()) { - reckon_new_support_point(*perim.support_point_generated, create_support_point_position(perim.b), float(-EPSILON), - Vec2f::Zero()); - } - if (perim.is_external_perimeter()) { - current_slice_ext_perims_lines.push_back(perim); - } - } - } - // DEBUG EXPORT, NOT USED NOW - // if (BR_bridge) { - // Lines scaledl; - // for (const auto &l : prev_layer_boundary.get_lines()) { - // scaledl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b)); - // } - - // Lines perimsl; - // for (const auto &l : current_slice_ext_perims_lines) { - // perimsl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b)); - // } - - // BoundingBox bb = get_extents(scaledl); - // bb.merge(get_extents(perimsl)); - - // ::Slic3r::SVG svg(debug_out_path( - // ("slice" + std::to_string(slice_idx) + "_" + std::to_string(layer_idx).c_str()).c_str()), - // get_extents(scaledl)); - // svg.draw(scaledl, "red", scale_(0.4)); - // svg.draw(perimsl, "blue", scale_(0.25)); - - - // svg.Close(); - // } + for (const auto &l : unstable_lines_per_slice[slice_idx]) { + assert(l.support_point_generated.has_value()); + reckon_new_support_point(*l.support_point_generated, create_support_point_position(l.b), float(-EPSILON), Vec2f::Zero()); } - LD current_slice_lines_distancer(current_slice_ext_perims_lines); + LD current_slice_lines_distancer({ext_perim_lines_per_slice[slice_idx].begin(), ext_perim_lines_per_slice[slice_idx].end()}); float unchecked_dist = params.min_distance_between_support_points + 1.0f; - for (const ExtrusionLine &line : current_slice_ext_perims_lines) { + for (const ExtrusionLine &line : current_slice_lines_distancer.get_lines()) { if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < params.curling_tolerance_limit) || line.len < EPSILON) { unchecked_dist += line.len; @@ -993,8 +1021,8 @@ std::tuple check_stability(const PrintObject *po, } } } - current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_ext_perims_lines.begin(), - current_slice_ext_perims_lines.end()); + current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_lines_distancer.get_lines().begin(), + current_slice_lines_distancer.get_lines().end()); } // slice iterations prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines); } // layer iterations @@ -1048,7 +1076,8 @@ void debug_export(const SupportPoints& support_points,const PartialObjects& obje std::tuple full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms) { - auto results = check_stability(po, cancel_func, params); + auto precomputed_slices_connections = precompute_slices_connections(po); + auto results = check_stability(po, precomputed_slices_connections, cancel_func, params); #ifdef DEBUG_FILES auto [supp_points, objects] = results; debug_export(supp_points, objects, "issues"); From 4b68fbd973069ca87d7aa5bd8881eb7f31c222d4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 27 Apr 2023 15:55:07 +0200 Subject: [PATCH 32/70] Follow-up https://github.com/Prusa-Development/PrusaSlicerPrivate/commit/88f4fa20df3e73b962b7800cc709b0ed43113338 - Fixed next Linux specific issue : Select any instance in place where is active a gizmo space on instance --- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 18 ++---------------- src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp | 1 - 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 98058ee17b..d10c7a5ff6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -24,14 +24,8 @@ GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filen bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) { - if (mouse_event.Moving()) { - // only for sure - m_mouse_left_down = false; - return false; - } if (mouse_event.LeftDown()) { if (m_hover_id != -1) { - m_mouse_left_down = true; Selection &selection = m_parent.get_selection(); if (selection.is_single_full_instance()) { // Rotate the object so the normal points downward: @@ -42,16 +36,8 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) return true; } } - else if (mouse_event.LeftUp()) { - if (m_mouse_left_down) { - // responsible for mouse left up after selecting plane - m_mouse_left_down = false; - return true; - } - - } - else if (mouse_event.Leaving()) - m_mouse_left_down = false; + else if (mouse_event.LeftUp()) + return m_hover_id != -1; return false; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp index ff44fcd2d0..e9e3c08d02 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp @@ -37,7 +37,6 @@ private: std::vector m_planes; std::vector> m_planes_casters; - bool m_mouse_left_down = false; // for detection left_up of this gizmo const ModelObject* m_old_model_object = nullptr; int m_old_instance_id{ -1 }; From 03608580c04842898031b5b3eeb1548443998592 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Apr 2023 12:14:06 +0200 Subject: [PATCH 33/70] Do not allow to change selection or printable state, using the right panel, while the SLA supports gizmo is open and in editing mode. --- src/slic3r/GUI/GUI_ObjectList.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a2ecbaea91..85d12bec9d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -85,6 +85,17 @@ ObjectList::ObjectList(wxWindow* parent) : // describe control behavior Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) { + // do not allow to change selection while the sla support gizmo is in editing mode + const GLGizmosManager& gizmos = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + if (gizmos.get_current_type() == GLGizmosManager::EType::SlaSupports && gizmos.is_in_editing_mode(true)) { + wxDataViewItemArray sels; + GetSelections(sels); + if (sels.size() > 1 || event.GetItem() != m_last_selected_item) { + select_item(m_last_selected_item); + return; + } + } + // detect the current mouse position here, to pass it to list_manipulation() method // if we detect it later, the user may have moved the mouse pointer while calculations are performed, and this would mess-up the HitTest() call performed into list_manipulation() // see: https://github.com/prusa3d/PrusaSlicer/issues/3802 @@ -4960,6 +4971,11 @@ void ObjectList::update_printable_state(int obj_idx, int instance_idx) void ObjectList::toggle_printable_state() { + // do not allow to toggle the printable state while the sla support gizmo is in editing mode + const GLGizmosManager& gizmos = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + if (gizmos.get_current_type() == GLGizmosManager::EType::SlaSupports && gizmos.is_in_editing_mode(true)) + return; + wxDataViewItemArray sels; GetSelections(sels); if (sels.IsEmpty()) From a7e17df25f66d3fb4fce213574ba25ebea1d238d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 12:59:20 +0200 Subject: [PATCH 34/70] ClipperLib: Further optimization of memory allocation using scalable_allocator. ClipperLib: SimplifyPolygon() - changed default winding number to positive, added strictly_simple parameter. ClipperUtlis simplify_polygons() - removed "remove_collinear" parameter --- src/clipper/clipper.cpp | 206 +++++++++++------------- src/clipper/clipper.hpp | 43 +++-- src/libslic3r/Arachne/WallToolPaths.cpp | 4 +- src/libslic3r/ClipperUtils.cpp | 25 ++- src/libslic3r/ClipperUtils.hpp | 4 +- 5 files changed, 137 insertions(+), 145 deletions(-) diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index c775a3226c..5da79d3a16 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -73,25 +73,6 @@ static int const Skip = -2; //edge that would otherwise close a path #define TOLERANCE (1.0e-20) #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) -// Output polygon. -struct OutRec { - int Idx; - bool IsHole; - bool IsOpen; - //The 'FirstLeft' field points to another OutRec that contains or is the - //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is - //parsed left from the current edge (owning OutRec) until the owner OutRec - //is found. This field simplifies sorting the polygons into a tree structure - //which reflects the parent/child relationships of all polygons. - //This field should be renamed Parent, and will be later. - OutRec *FirstLeft; - // Used only by void Clipper::BuildResult2(PolyTree& polytree) - PolyNode *PolyNd; - // Linked list of output points, dynamically allocated. - OutPt *Pts; - OutPt *BottomPt; -}; - //------------------------------------------------------------------------------ inline IntPoint IntPoint2d(cInt x, cInt y) @@ -1061,8 +1042,7 @@ IntRect ClipperBase::GetBounds() Clipper::Clipper(int initOptions) : ClipperBase(), m_OutPtsFree(nullptr), - m_OutPtsChunkSize(32), - m_OutPtsChunkLast(32), + m_OutPtsChunkLast(m_OutPtsChunkSize), m_ActiveEdges(nullptr), m_SortedEdges(nullptr) { @@ -1153,23 +1133,23 @@ bool Clipper::ExecuteInternal() //FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers? //FIXME Vojtech: The area is calculated with floats, it may not be numerically stable! { - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); + for (OutRec &outRec : m_PolyOuts) + if (outRec.Pts && !outRec.IsOpen && (outRec.IsHole ^ m_ReverseOutput) == (Area(outRec) > 0)) + ReversePolyPtLinks(outRec.Pts); } JoinCommonEdges(); //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() { - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts) { - if (outRec->IsOpen) + for (OutRec &outRec : m_PolyOuts) + if (outRec.Pts) { + if (outRec.IsOpen) // Removes duplicate points. - FixupOutPolyline(*outRec); + FixupOutPolyline(outRec); else // Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex. - FixupOutPolygon(*outRec); + FixupOutPolygon(outRec); } } // For each polygon, search for exactly duplicate non-successive points. @@ -1194,22 +1174,18 @@ OutPt* Clipper::AllocateOutPt() m_OutPtsFree = pt->Next; } else if (m_OutPtsChunkLast < m_OutPtsChunkSize) { // Get a point from the last chunk. - pt = m_OutPts.back() + (m_OutPtsChunkLast ++); + pt = &m_OutPts.back()[m_OutPtsChunkLast ++]; } else { // The last chunk is full. Allocate a new one. - m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]); + m_OutPts.push_back({}); m_OutPtsChunkLast = 1; - pt = m_OutPts.back(); + pt = &m_OutPts.back().front(); } return pt; } void Clipper::DisposeAllOutRecs() { - for (OutPt *pts : m_OutPts) - delete[] pts; - for (OutRec *rec : m_PolyOuts) - delete rec; m_OutPts.clear(); m_OutPtsFree = nullptr; m_OutPtsChunkLast = m_OutPtsChunkSize; @@ -1832,7 +1808,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) } //------------------------------------------------------------------------------ -void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) { bool IsHole = false; TEdge *e2 = e->PrevInAEL; @@ -1842,7 +1818,7 @@ void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const { IsHole = !IsHole; if (! outrec->FirstLeft) - outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; + outrec->FirstLeft = &m_PolyOuts[e2->OutIdx]; } e2 = e2->PrevInAEL; } @@ -1883,18 +1859,18 @@ bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) OutRec* Clipper::GetOutRec(int Idx) { - OutRec* outrec = m_PolyOuts[Idx]; - while (outrec != m_PolyOuts[outrec->Idx]) - outrec = m_PolyOuts[outrec->Idx]; + OutRec* outrec = &m_PolyOuts[Idx]; + while (outrec != &m_PolyOuts[outrec->Idx]) + outrec = &m_PolyOuts[outrec->Idx]; return outrec; } //------------------------------------------------------------------------------ -void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) { //get the start and ends of both output polygons ... - OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; - OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + OutRec *outRec1 = &m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = &m_PolyOuts[e2->OutIdx]; OutRec *holeStateRec; if (Param1RightOfParam2(outRec1, outRec2)) @@ -1991,16 +1967,16 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const OutRec* Clipper::CreateOutRec() { - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size()-1; - return result; + m_PolyOuts.push_back({}); + OutRec &result = m_PolyOuts.back(); + result.IsHole = false; + result.IsOpen = false; + result.FirstLeft = 0; + result.Pts = 0; + result.BottomPt = 0; + result.PolyNd = 0; + result.Idx = (int)m_PolyOuts.size()-1; + return &result; } //------------------------------------------------------------------------------ @@ -2022,7 +1998,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) return newOp; } else { - OutRec *outRec = m_PolyOuts[e->OutIdx]; + OutRec *outRec = &m_PolyOuts[e->OutIdx]; //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' OutPt* op = outRec->Pts; @@ -2045,7 +2021,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) OutPt* Clipper::GetLastOutPt(TEdge *e) { - OutRec *outRec = m_PolyOuts[e->OutIdx]; + OutRec *outRec = &m_PolyOuts[e->OutIdx]; if (e->Side == esLeft) return outRec->Pts; else @@ -2216,7 +2192,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { Direction dir; cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen); + bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx].IsOpen); GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); @@ -2778,12 +2754,12 @@ int PointCount(OutPt *Pts) void Clipper::BuildResult(Paths &polys) { polys.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - assert(! outRec->IsOpen); - if (!outRec->Pts) continue; + assert(! outRec.IsOpen); + if (!outRec.Pts) continue; Path pg; - OutPt* p = outRec->Pts->Prev; + OutPt* p = outRec.Pts->Prev; int cnt = PointCount(p); if (cnt < 2) continue; pg.reserve(cnt); @@ -2802,31 +2778,31 @@ void Clipper::BuildResult2(PolyTree& polytree) polytree.Clear(); polytree.AllNodes.reserve(m_PolyOuts.size()); //add each output polygon/contour to polytree ... - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) + int cnt = PointCount(outRec.Pts); + if ((outRec.IsOpen && cnt < 2) || (!outRec.IsOpen && cnt < 3)) // Ignore an invalid output loop or a polyline. continue; //skip OutRecs that (a) contain outermost polygons or //(b) already have the correct owner/child linkage ... - if (outRec->FirstLeft && - (outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) { - OutRec* orfl = outRec->FirstLeft; - while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts)) + if (outRec.FirstLeft && + (outRec.IsHole == outRec.FirstLeft->IsHole || ! outRec.FirstLeft->Pts)) { + OutRec* orfl = outRec.FirstLeft; + while (orfl && ((orfl->IsHole == outRec.IsHole) || !orfl->Pts)) orfl = orfl->FirstLeft; - outRec->FirstLeft = orfl; + outRec.FirstLeft = orfl; } //nb: polytree takes ownership of all the PolyNodes polytree.AllNodes.emplace_back(PolyNode()); PolyNode* pn = &polytree.AllNodes.back(); - outRec->PolyNd = pn; + outRec.PolyNd = pn; pn->Parent = 0; pn->Index = 0; pn->Contour.reserve(cnt); - OutPt *op = outRec->Pts->Prev; + OutPt *op = outRec.Pts->Prev; for (int j = 0; j < cnt; j++) { pn->Contour.emplace_back(op->Pt); @@ -2836,18 +2812,18 @@ void Clipper::BuildResult2(PolyTree& polytree) //fixup PolyNode links etc ... polytree.Childs.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - if (!outRec->PolyNd) continue; - if (outRec->IsOpen) + if (!outRec.PolyNd) continue; + if (outRec.IsOpen) { - outRec->PolyNd->m_IsOpen = true; - polytree.AddChild(*outRec->PolyNd); + outRec.PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec.PolyNd); } - else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) - outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else if (outRec.FirstLeft && outRec.FirstLeft->PolyNd) + outRec.FirstLeft->PolyNd->AddChild(*outRec.PolyNd); else - polytree.AddChild(*outRec->PolyNd); + polytree.AddChild(*outRec.PolyNd); } } //------------------------------------------------------------------------------ @@ -3193,26 +3169,26 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) //---------------------------------------------------------------------- // This is potentially very expensive! O(n^3)! -void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) { //tests if NewOutRec contains the polygon before reassigning FirstLeft - for (OutRec *outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - if (!outRec->Pts || !outRec->FirstLeft) continue; - OutRec* firstLeft = outRec->FirstLeft; + if (!outRec.Pts || !outRec.FirstLeft) continue; + OutRec* firstLeft = outRec.FirstLeft; // Skip empty polygons. while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) - outRec->FirstLeft = NewOutRec; + if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec.Pts, NewOutRec->Pts)) + outRec.FirstLeft = NewOutRec; } } //---------------------------------------------------------------------- -void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const +void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) { //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon - for (OutRec *outRec : m_PolyOuts) - if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + for (OutRec &outRec : m_PolyOuts) + if (outRec.FirstLeft == OldOutRec) outRec.FirstLeft = NewOutRec; } //---------------------------------------------------------------------- @@ -3253,13 +3229,13 @@ void Clipper::JoinCommonEdges() if (m_UsingPolyTree) for (size_t j = 0; j < m_PolyOuts.size() - 1; j++) { - OutRec* oRec = m_PolyOuts[j]; - OutRec* firstLeft = oRec->FirstLeft; + OutRec &oRec = m_PolyOuts[j]; + OutRec* firstLeft = oRec.FirstLeft; while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (!oRec->Pts || firstLeft != outRec1 || - oRec->IsHole == outRec1->IsHole) continue; - if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2)) - oRec->FirstLeft = outRec2; + if (!oRec.Pts || firstLeft != outRec1 || + oRec.IsHole == outRec1->IsHole) continue; + if (Poly2ContainsPoly1(oRec.Pts, join.OutPt2)) + oRec.FirstLeft = outRec2; } if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) @@ -3771,13 +3747,13 @@ void Clipper::DoSimplePolygons() size_t i = 0; while (i < m_PolyOuts.size()) { - OutRec* outrec = m_PolyOuts[i++]; - OutPt* op = outrec->Pts; - if (!op || outrec->IsOpen) continue; + OutRec &outrec = m_PolyOuts[i++]; + OutPt* op = outrec.Pts; + if (!op || outrec.IsOpen) continue; do //for each Pt in Polygon until duplicate found do ... { OutPt* op2 = op->Next; - while (op2 != outrec->Pts) + while (op2 != outrec.Pts) { if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) { @@ -3789,37 +3765,37 @@ void Clipper::DoSimplePolygons() op2->Prev = op3; op3->Next = op2; - outrec->Pts = op; + outrec.Pts = op; OutRec* outrec2 = CreateOutRec(); outrec2->Pts = op2; UpdateOutPtIdxs(*outrec2); - if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + if (Poly2ContainsPoly1(outrec2->Pts, outrec.Pts)) { //OutRec2 is contained by OutRec1 ... - outrec2->IsHole = !outrec->IsHole; - outrec2->FirstLeft = outrec; + outrec2->IsHole = !outrec.IsHole; + outrec2->FirstLeft = &outrec; // For each m_PolyOuts, replace FirstLeft from outRec2 to outrec. - if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, &outrec); } else - if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + if (Poly2ContainsPoly1(outrec.Pts, outrec2->Pts)) { //OutRec1 is contained by OutRec2 ... - outrec2->IsHole = outrec->IsHole; - outrec->IsHole = !outrec2->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - outrec->FirstLeft = outrec2; + outrec2->IsHole = outrec.IsHole; + outrec.IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec.FirstLeft; + outrec.FirstLeft = outrec2; // For each m_PolyOuts, replace FirstLeft from outrec to outrec2. - if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + if (m_UsingPolyTree) FixupFirstLefts2(&outrec, outrec2); } else { //the 2 polygons are separate ... - outrec2->IsHole = outrec->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; + outrec2->IsHole = outrec.IsHole; + outrec2->FirstLeft = outrec.FirstLeft; // For each polygon of m_PolyOuts, replace FirstLeft from outrec to outrec2 if the polygon is inside outRec2. //FIXME This is potentially very expensive! O(n^3)! - if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + if (m_UsingPolyTree) FixupFirstLefts1(&outrec, outrec2); } op2 = op; //ie get ready for the Next iteration } @@ -3827,7 +3803,7 @@ void Clipper::DoSimplePolygons() } op = op->Next; } - while (op != outrec->Pts); + while (op != outrec.Pts); } } //------------------------------------------------------------------------------ @@ -3845,10 +3821,10 @@ void ReversePaths(Paths& p) } //------------------------------------------------------------------------------ -Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType) +Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType, bool strictly_simple /* = true */) { Clipper c; - c.StrictlySimple(true); + c.StrictlySimple(strictly_simple); c.AddPath(in_poly, ptSubject, true); Paths out; c.Execute(ctUnion, out, fillType, fillType); diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index d190d09b5d..c88545454e 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -52,6 +52,7 @@ //use_deprecated: Enables temporary support for the obsolete functions //#define use_deprecated +#include #include #include #include @@ -199,7 +200,8 @@ double Area(const Path &poly); inline bool Orientation(const Path &poly) { return Area(poly) >= 0; } int PointInPolygon(const IntPoint &pt, const Path &path); -Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftEvenOdd); +// Union with "strictly simple" fix enabled. +Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftNonZero, bool strictly_simple = true); void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); void CleanPolygon(Path& poly, double distance = 1.415); @@ -284,7 +286,25 @@ enum EdgeSide { esLeft = 1, esRight = 2}; using OutPts = std::vector>; - struct OutRec; + // Output polygon. + struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + //The 'FirstLeft' field points to another OutRec that contains or is the + //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is + //parsed left from the current edge (owning OutRec) until the owner OutRec + //is found. This field simplifies sorting the polygons into a tree structure + //which reflects the parent/child relationships of all polygons. + //This field should be renamed Parent, and will be later. + OutRec* FirstLeft; + // Used only by void Clipper::BuildResult2(PolyTree& polytree) + PolyNode* PolyNd; + // Linked list of output points, dynamically allocated. + OutPt* Pts; + OutPt* BottomPt; + }; + struct Join { Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {} @@ -432,12 +452,12 @@ protected: private: // Output polygons. - std::vector> m_PolyOuts; + std::deque> m_PolyOuts; // Output points, allocated by a continuous sets of m_OutPtsChunkSize. - std::vector> m_OutPts; + static constexpr const size_t m_OutPtsChunkSize = 32; + std::deque, Allocator>> m_OutPts; // List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk. OutPt *m_OutPtsFree; - size_t m_OutPtsChunkSize; size_t m_OutPtsChunkLast; std::vector> m_Joins; @@ -482,7 +502,7 @@ private: void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutRec* GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2) const; + void AppendPolygon(TEdge *e1, TEdge *e2); void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); @@ -498,7 +518,7 @@ private: void ProcessEdgesAtTopOfScanbeam(const cInt topY); void BuildResult(Paths& polys); void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *outrec) const; + void SetHoleState(TEdge *e, OutRec *outrec); bool FixupIntersectionOrder(); void FixupOutPolygon(OutRec &outrec); void FixupOutPolyline(OutRec &outrec); @@ -508,8 +528,8 @@ private: bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft); void JoinCommonEdges(); void DoSimplePolygons(); - void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; - void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec); #ifdef CLIPPERLIB_USE_XYZ void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); #endif @@ -567,10 +587,11 @@ class clipperException : public std::exception }; //------------------------------------------------------------------------------ +// Union with "strictly simple" fix enabled. template -inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftEvenOdd) { +inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftNonZero, bool strictly_simple = true) { Clipper c; - c.StrictlySimple(true); + c.StrictlySimple(strictly_simple); c.AddPaths(std::forward(in_polys), ptSubject, true); Paths out; c.Execute(ctUnion, out, fillType, fillType); diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index fce69d5e4c..6c5dafdac4 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -232,7 +232,7 @@ std::unique_ptr cre void fixSelfIntersections(const coord_t epsilon, Polygons &thiss) { if (epsilon < 1) { - ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); + ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd); return; } @@ -273,7 +273,7 @@ void fixSelfIntersections(const coord_t epsilon, Polygons &thiss) } } - ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); + ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd); } /*! diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 95a5337185..d10e14cc53 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -964,21 +964,18 @@ Polygons union_pt_chained_outside_in(const Polygons &subject) return retval; } -Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) +Polygons simplify_polygons(const Polygons &subject) { CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); ClipperLib::Paths output; - if (preserve_collinear) { - ClipperLib::Clipper c; - c.PreserveCollinear(true); - c.StrictlySimple(true); - c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); - c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); - } else { - output = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(subject), ClipperLib::pftNonZero); - } - + ClipperLib::Clipper c; +// c.PreserveCollinear(true); + //FIXME StrictlySimple is very expensive! Is it needed? + c.StrictlySimple(true); + c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); + c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + // convert into Slic3r polygons return to_polygons(std::move(output)); } @@ -987,12 +984,10 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear { CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); - if (! preserve_collinear) - return union_ex(simplify_polygons(subject, false)); - ClipperLib::PolyTree polytree; ClipperLib::Clipper c; - c.PreserveCollinear(true); +// c.PreserveCollinear(true); + //FIXME StrictlySimple is very expensive! Is it needed? c.StrictlySimple(true); c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index aaa06107da..ab967fd8fb 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -596,8 +596,8 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval) /* OTHER */ -Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); -Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false); +Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject); +Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject); Polygons top_level_islands(const Slic3r::Polygons &polygons); From 0836b06c7360462e3b8719864de4a446887c0d9e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 15:48:24 +0200 Subject: [PATCH 35/70] Little refactoring of douglas_peucker() --- src/libslic3r/ExPolygon.cpp | 4 ++-- src/libslic3r/Geometry.cpp | 16 ++++++------- src/libslic3r/MultiPoint.cpp | 2 +- src/libslic3r/MultiPoint.hpp | 2 +- src/libslic3r/Polygon.cpp | 45 +++++++++++++++++++++++++----------- src/libslic3r/Polygon.hpp | 3 ++- src/libslic3r/Polyline.cpp | 2 +- 7 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 42f026e0b2..19489bddbc 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -184,14 +184,14 @@ Polygons ExPolygon::simplify_p(double tolerance) const { Polygon p = this->contour; p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + p.points = MultiPoint::douglas_peucker(p.points, tolerance); p.points.pop_back(); pp.emplace_back(std::move(p)); } // holes for (Polygon p : this->holes) { p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + p.points = MultiPoint::douglas_peucker(p.points, tolerance); p.points.pop_back(); pp.emplace_back(std::move(p)); } diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 5542d73eee..f2860ea8e2 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -52,15 +52,15 @@ template bool contains(const ExPolygons &vector, const Point &point); void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) { - Polygons pp; - for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) { - Polygon p = *it; - p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); - p.points.pop_back(); - pp.push_back(p); + Polygons simplified_raw; + for (const Polygon &source_polygon : polygons) { + Points simplified = MultiPoint::douglas_peucker(to_polyline(source_polygon).points, tolerance); + if (simplified.size() > 3) { + simplified.pop_back(); + simplified_raw.push_back(Polygon{ std::move(simplified) }); + } } - *retval = Slic3r::simplify_polygons(pp); + *retval = Slic3r::simplify_polygons(simplified_raw); } double linint(double value, double oldmin, double oldmax, double newmin, double newmax) diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index bb4d62cc0b..ce54a54c00 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -103,7 +103,7 @@ bool MultiPoint::remove_duplicate_points() return false; } -Points MultiPoint::_douglas_peucker(const Points &pts, const double tolerance) +Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) { Points result_pts; double tolerance_sq = tolerance * tolerance; diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 4cf4b5e140..62b53255b4 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -81,7 +81,7 @@ public: } } - static Points _douglas_peucker(const Points &points, const double tolerance); + static Points douglas_peucker(const Points &points, const double tolerance); static Points visivalingam(const Points& pts, const double& tolerance); inline auto begin() { return points.begin(); } diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 299e22adcd..88ac1b03f8 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -96,7 +96,7 @@ bool Polygon::make_clockwise() void Polygon::douglas_peucker(double tolerance) { this->points.push_back(this->points.front()); - Points p = MultiPoint::_douglas_peucker(this->points, tolerance); + Points p = MultiPoint::douglas_peucker(this->points, tolerance); p.pop_back(); this->points = std::move(p); } @@ -110,7 +110,7 @@ Polygons Polygon::simplify(double tolerance) const // on the whole polygon Points points = this->points; points.push_back(points.front()); - Polygon p(MultiPoint::_douglas_peucker(points, tolerance)); + Polygon p(MultiPoint::douglas_peucker(points, tolerance)); p.points.pop_back(); Polygons pp; @@ -577,23 +577,40 @@ void remove_collinear(Polygons &polys) remove_collinear(poly); } -Polygons polygons_simplify(const Polygons &source_polygons, double tolerance) +static inline void simplify_polygon_impl(const Points &points, double tolerance, bool strictly_simple, Polygons &out) +{ + Points simplified = MultiPoint::douglas_peucker(points, tolerance); + // then remove the last (repeated) point. + simplified.pop_back(); + // Simplify the decimated contour by ClipperLib. + bool ccw = ClipperLib::Area(simplified) > 0.; + for (Points& path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero, strictly_simple)) { + if (!ccw) + // ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW. + std::reverse(path.begin(), path.end()); + out.emplace_back(std::move(path)); + } +} + +Polygons polygons_simplify(Polygons &&source_polygons, double tolerance, bool strictly_simple /* = true */) +{ + Polygons out; + out.reserve(source_polygons.size()); + for (Polygon &source_polygon : source_polygons) { + // Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline), + source_polygon.points.emplace_back(source_polygon.points.front()); + simplify_polygon_impl(source_polygon.points, tolerance, strictly_simple, out); + } + return out; +} + +Polygons polygons_simplify(const Polygons &source_polygons, double tolerance, bool strictly_simple /* = true */) { Polygons out; out.reserve(source_polygons.size()); for (const Polygon &source_polygon : source_polygons) { // Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline), - Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance); - // then remove the last (repeated) point. - simplified.pop_back(); - // Simplify the decimated contour by ClipperLib. - bool ccw = ClipperLib::Area(simplified) > 0.; - for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero)) { - if (! ccw) - // ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW. - std::reverse(path.begin(), path.end()); - out.emplace_back(std::move(path)); - } + simplify_polygon_impl(to_polyline(source_polygon).points, tolerance, strictly_simple, out); } return out; } diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index bf4a087b0d..e0c3958fd9 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -149,7 +149,8 @@ inline void polygons_append(Polygons &dst, Polygons &&src) } } -Polygons polygons_simplify(const Polygons &polys, double tolerance); +Polygons polygons_simplify(Polygons &&polys, double tolerance, bool strictly_simple = true); +Polygons polygons_simplify(const Polygons &polys, double tolerance, bool strictly_simple = true); inline void polygons_rotate(Polygons &polys, double angle) { diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 5743e38bd5..5247365752 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -110,7 +110,7 @@ Points Polyline::equally_spaced_points(double distance) const void Polyline::simplify(double tolerance) { - this->points = MultiPoint::_douglas_peucker(this->points, tolerance); + this->points = MultiPoint::douglas_peucker(this->points, tolerance); } #if 0 From 31a5daa5e4ab315d668b1b63f1691ae4961235a6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 15:53:08 +0200 Subject: [PATCH 36/70] Optimization of triangle mesh slicing: scalable_allocator and hashing of shared mutexes. --- src/libslic3r/TriangleMeshSlicer.cpp | 42 ++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 460cd901e9..0261b6121b 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -15,6 +15,9 @@ #include #include +#include + +#include #ifndef NDEBUG // #define EXPENSIVE_DEBUG_CHECKS @@ -139,7 +142,7 @@ public: #endif }; -using IntersectionLines = std::vector; +using IntersectionLines = std::vector>; enum class FacetSliceType { NoSlice = 0, @@ -351,6 +354,21 @@ inline FacetSliceType slice_facet( return FacetSliceType::NoSlice; } +class LinesMutexes { +public: + std::mutex& operator()(size_t slice_id) { + ankerl::unordered_dense::hash hash; + return m_mutexes[hash(slice_id) % m_mutexes.size()].mutex; + } + +private: + struct CacheLineAlignedMutex + { + alignas(std::hardware_destructive_interference_size) std::mutex mutex; + }; + std::array m_mutexes; +}; + template void slice_facet_at_zs( // Scaled or unscaled vertices. transform_vertex_fn may scale zs. @@ -361,7 +379,7 @@ void slice_facet_at_zs( // Scaled or unscaled zs. If vertices have their zs scaled or transform_vertex_fn scales them, then zs have to be scaled as well. const std::vector &zs, std::vector &lines, - std::array &lines_mutex) + LinesMutexes &lines_mutex) { stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) }; @@ -380,7 +398,7 @@ void slice_facet_at_zs( if (min_z != max_z && slice_facet(*it, vertices, indices, edge_ids, idx_vertex_lowest, false, il) == FacetSliceType::Slicing) { assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal); size_t slice_id = it - zs.begin(); - boost::lock_guard l(lines_mutex[slice_id % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(slice_id)); lines[slice_id].emplace_back(il); } } @@ -395,8 +413,8 @@ static inline std::vector slice_make_lines( const std::vector &zs, const ThrowOnCancel throw_on_cancel_fn) { - std::vector lines(zs.size(), IntersectionLines()); - std::array lines_mutex; + std::vector lines(zs.size(), IntersectionLines{}); + LinesMutexes lines_mutex; tbb::parallel_for( tbb::blocked_range(0, int(indices.size())), [&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range &range) { @@ -475,7 +493,7 @@ void slice_facet_with_slabs( const int num_edges, const std::vector &zs, SlabLines &lines, - std::array &lines_mutex) + LinesMutexes &lines_mutex) { const stl_triangle_vertex_indices &indices = mesh_triangles[facet_idx]; stl_vertex vertices[3] { mesh_vertices[indices(0)], mesh_vertices[indices(1)], mesh_vertices[indices(2)] }; @@ -494,7 +512,7 @@ void slice_facet_with_slabs( auto emit_slab_edge = [&lines, &lines_mutex](IntersectionLine il, size_t slab_id, bool reverse) { if (reverse) il.reverse(); - boost::lock_guard l(lines_mutex[(slab_id + lines_mutex.size() / 2) % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(slab_id)); lines.between_slices[slab_id].emplace_back(il); }; @@ -530,7 +548,7 @@ void slice_facet_with_slabs( }; // Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining. // if (! ProjectionFromTop) il.reverse(); - boost::lock_guard l(lines_mutex[line_id % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(line_id)); lines.at_slice[line_id].emplace_back(il); } } else { @@ -649,7 +667,7 @@ void slice_facet_with_slabs( if (! ProjectionFromTop) il.reverse(); size_t line_id = it - zs.begin(); - boost::lock_guard l(lines_mutex[line_id % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(line_id)); lines.at_slice[line_id].emplace_back(il); } } @@ -804,8 +822,8 @@ inline std::pair slice_slabs_make_lines( std::pair out; SlabLines &lines_top = out.first; SlabLines &lines_bottom = out.second; - std::array lines_mutex_top; - std::array lines_mutex_bottom; + LinesMutexes lines_mutex_top; + LinesMutexes lines_mutex_bottom; if (top) { lines_top.at_slice.assign(zs.size(), IntersectionLines()); @@ -1540,7 +1558,7 @@ static std::vector make_slab_loops( } // Used to cut the mesh into two halves. -static ExPolygons make_expolygons_simple(std::vector &lines) +static ExPolygons make_expolygons_simple(IntersectionLines &lines) { ExPolygons slices; Polygons holes; From 04f557693b8369d7019e861a4bf26bab44ee4464 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 16:26:16 +0200 Subject: [PATCH 37/70] Follow-up to 31a5daa5e4ab315d668b1b63f1691ae4961235a6: Fixed missing include --- src/libslic3r/TriangleMeshSlicer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 0261b6121b..2c65702192 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include From a077f669ee5409c7550df538dd00c6b294d7019f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 16:32:03 +0200 Subject: [PATCH 38/70] Slight optimization of distance_to_squared() --- src/libslic3r/Line.hpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 1edd1f5e9f..d90757bedd 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -40,11 +40,12 @@ template auto get_b(L &&l) { return Traits>::get_b(l) // Distance to the closest point of line. template -double distance_to_squared(const L &line, const Vec, Scalar> &point, Vec, Scalar> *nearest_point) +inline double distance_to_squared(const L &line, const Vec, Scalar> &point, Vec, Scalar> *nearest_point) { - const Vec, double> v = (get_b(line) - get_a(line)).template cast(); - const Vec, double> va = (point - get_a(line)).template cast(); - const double l2 = v.squaredNorm(); // avoid a sqrt + using VecType = Vec, double>; + const VecType v = (get_b(line) - get_a(line)).template cast(); + const VecType va = (point - get_a(line)).template cast(); + const double l2 = v.squaredNorm(); if (l2 == 0.0) { // a == b case *nearest_point = get_a(line); @@ -53,19 +54,20 @@ double distance_to_squared(const L &line, const Vec, Scalar> &point, V // Consider the line extending the segment, parameterized as a + t (b - a). // We find projection of this point onto the line. // It falls where t = [(this-a) . (b-a)] / |b-a|^2 - const double t = va.dot(v) / l2; + const double t = va.dot(v); if (t <= 0.0) { // beyond the 'a' end of the segment *nearest_point = get_a(line); return va.squaredNorm(); - } else if (t >= 1.0) { + } else if (t >= l2) { // beyond the 'b' end of the segment *nearest_point = get_b(line); return (point - get_b(line)).template cast().squaredNorm(); } - *nearest_point = (get_a(line).template cast() + t * v).template cast>(); - return (t * v - va).squaredNorm(); + const VecType w = ((t / l2) * v).eval(); + *nearest_point = (get_a(line).template cast() + w).template cast>(); + return (w - va).squaredNorm(); } // Distance to the closest point of line. From e7f4704ddc1c64a55b1a9bc57ecb3ffc3e6631b6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 16:35:39 +0200 Subject: [PATCH 39/70] Organic supports, TreeModelVolumes: Cheaper simplification. --- src/libslic3r/TreeModelVolumes.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index 5a1fcec561..3c82b5c6c2 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -34,6 +34,8 @@ using namespace std::literals; // had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL() #define error_level_not_in_cache error +static constexpr const bool polygons_strictly_simple = false; + TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object) { const PrintConfig &print_config = print_object.print()->config(); @@ -175,7 +177,7 @@ TreeModelVolumes::TreeModelVolumes( tbb::parallel_for(tbb::blocked_range(num_raft_layers, num_layers, std::min(1, std::max(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))), [&](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx - num_raft_layers)->lslices, mesh_settings.resolution)); + outlines[layer_idx] = polygons_simplify(to_polygons(print_object.get_layer(layer_idx - num_raft_layers)->lslices), mesh_settings.resolution, polygons_strictly_simple); }); } #endif @@ -585,7 +587,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex if (processing_last_mesh) { if (! dst.empty()) collisions = union_(collisions, dst); - dst = polygons_simplify(collisions, min_resolution); + dst = polygons_simplify(collisions, min_resolution, polygons_strictly_simple); } else append(dst, std::move(collisions)); throw_on_cancel(); @@ -609,7 +611,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex if (processing_last_mesh) { if (! dst.empty()) placable = union_(placable, dst); - dst = polygons_simplify(placable, min_resolution); + dst = polygons_simplify(placable, min_resolution, polygons_strictly_simple); } else append(dst, placable); throw_on_cancel(); @@ -657,7 +659,7 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vectorgetCollision(m_increase_until_radius, layer_idx, false)), 5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution), - m_min_resolution)); + m_min_resolution, polygons_strictly_simple)); throw_on_cancel(); } } @@ -744,7 +746,7 @@ void TreeModelVolumes::calculateAvoidance(const std::vector &ke ClipperLib::jtRound, m_min_resolution)); if (task.to_model) latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel)); - latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution); + latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution, polygons_strictly_simple); data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance); throw_on_cancel(); } @@ -865,12 +867,12 @@ void TreeModelVolumes::calculateWallRestrictions(const std::vector Date: Tue, 2 May 2023 18:17:08 +0200 Subject: [PATCH 40/70] ClipperLib: emplace_back() instead of push_back(). --- src/clipper/clipper.cpp | 90 ++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index 5da79d3a16..3691877eea 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -112,7 +112,7 @@ int PolyTree::Total() const void PolyNode::AddChild(PolyNode& child) { unsigned cnt = (unsigned)Childs.size(); - Childs.push_back(&child); + Childs.emplace_back(&child); child.Parent = this; child.Index = cnt; } @@ -674,7 +674,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) locMin.RightBound = E; E->WindDelta = 0; Result = ProcessBound(E, NextIsForward); - m_MinimaList.push_back(locMin); + m_MinimaList.emplace_back(locMin); } return Result; } @@ -896,7 +896,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b E->NextInLML = E->Next; E = E->Next; } - m_MinimaList.push_back(locMin); + m_MinimaList.emplace_back(locMin); return true; } @@ -949,7 +949,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b locMin.LeftBound = 0; else if (locMin.RightBound->OutIdx == Skip) locMin.RightBound = 0; - m_MinimaList.push_back(locMin); + m_MinimaList.emplace_back(locMin); if (!leftBoundIsForward) E = E2; } return true; @@ -1177,7 +1177,7 @@ OutPt* Clipper::AllocateOutPt() pt = &m_OutPts.back()[m_OutPtsChunkLast ++]; } else { // The last chunk is full. Allocate a new one. - m_OutPts.push_back({}); + m_OutPts.emplace_back(); m_OutPtsChunkLast = 1; pt = &m_OutPts.back().front(); } @@ -1967,7 +1967,7 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) OutRec* Clipper::CreateOutRec() { - m_PolyOuts.push_back({}); + m_PolyOuts.emplace_back(); OutRec &result = m_PolyOuts.back(); result.IsHole = false; result.IsOpen = false; @@ -2576,7 +2576,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsMaximaEdge) { - if (m_StrictSimple) m_Maxima.push_back(e->Top.x()); + if (m_StrictSimple) m_Maxima.emplace_back(e->Top.x()); TEdge* ePrev = e->PrevInAEL; DoMaxima(e); if( !ePrev ) e = m_ActiveEdges; @@ -3349,7 +3349,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType break; } newNode->Contour.reserve(highI + 1); - newNode->Contour.push_back(path[0]); + newNode->Contour.emplace_back(path[0]); int j = 0, k = 0; for (int i = 1; i <= highI; i++) { bool same = false; @@ -3362,7 +3362,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType if (same) continue; j++; - newNode->Contour.push_back(path[i]); + newNode->Contour.emplace_back(path[i]); if (path[i].y() > newNode->Contour[k].y() || (path[i].y() == newNode->Contour[k].y() && path[i].x() < newNode->Contour[k].x())) k = j; @@ -3490,7 +3490,7 @@ void ClipperOffset::DoOffset(double delta) { PolyNode& node = *m_polyNodes.Childs[i]; if (node.m_endtype == etClosedPolygon) - m_destPolys.push_back(node.Contour); + m_destPolys.emplace_back(node.Contour); } return; } @@ -3532,7 +3532,7 @@ void ClipperOffset::DoOffset(double delta) double X = 1.0, Y = 0.0; for (cInt j = 1; j <= steps; j++) { - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[0].x() + X * delta), Round(m_srcPoly[0].y() + Y * delta))); double X2 = X; @@ -3545,7 +3545,7 @@ void ClipperOffset::DoOffset(double delta) double X = -1.0, Y = -1.0; for (int j = 0; j < 4; ++j) { - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[0].x() + X * delta), Round(m_srcPoly[0].y() + Y * delta))); if (X < 0) X = 1; @@ -3553,32 +3553,32 @@ void ClipperOffset::DoOffset(double delta) else X = -1; } } - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); continue; } //build m_normals ... m_normals.clear(); m_normals.reserve(len); for (int j = 0; j < len - 1; ++j) - m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + m_normals.emplace_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) - m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + m_normals.emplace_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); else - m_normals.push_back(DoublePoint(m_normals[len - 2])); + m_normals.emplace_back(DoublePoint(m_normals[len - 2])); if (node.m_endtype == etClosedPolygon) { int k = len - 1; for (int j = 0; j < len; ++j) OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); } else if (node.m_endtype == etClosedLine) { int k = len - 1; for (int j = 0; j < len; ++j) OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); m_destPoly.clear(); //re-build m_normals ... DoublePoint n = m_normals[len -1]; @@ -3588,7 +3588,7 @@ void ClipperOffset::DoOffset(double delta) k = 0; for (int j = len - 1; j >= 0; j--) OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); } else { @@ -3601,9 +3601,9 @@ void ClipperOffset::DoOffset(double delta) { int j = len - 1; pt1 = IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * delta), Round(m_srcPoly[j].y() + m_normals[j].y() * delta)); - m_destPoly.push_back(pt1); + m_destPoly.emplace_back(pt1); pt1 = IntPoint2d(Round(m_srcPoly[j].x() - m_normals[j].x() * delta), Round(m_srcPoly[j].y() - m_normals[j].y() * delta)); - m_destPoly.push_back(pt1); + m_destPoly.emplace_back(pt1); } else { @@ -3628,9 +3628,9 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { pt1 = IntPoint2d(Round(m_srcPoly[0].x() - m_normals[0].x() * delta), Round(m_srcPoly[0].y() - m_normals[0].y() * delta)); - m_destPoly.push_back(pt1); + m_destPoly.emplace_back(pt1); pt1 = IntPoint2d(Round(m_srcPoly[0].x() + m_normals[0].x() * delta), Round(m_srcPoly[0].y() + m_normals[0].y() * delta)); - m_destPoly.push_back(pt1); + m_destPoly.emplace_back(pt1); } else { @@ -3641,7 +3641,7 @@ void ClipperOffset::DoOffset(double delta) else DoRound(0, 1); } - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); } } } @@ -3657,7 +3657,7 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) double cosA = (m_normals[k].x() * m_normals[j].x() + m_normals[j].y() * m_normals[k].y() ); if (cosA > 0) // angle => 0 degrees { - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); return; } @@ -3668,10 +3668,10 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) if (m_sinA * m_delta < 0) { - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); - m_destPoly.push_back(m_srcPoly[j]); - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), + m_destPoly.emplace_back(m_srcPoly[j]); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } else @@ -3695,10 +3695,10 @@ void ClipperOffset::DoSquare(int j, int k) { double dx = std::tan(std::atan2(m_sinA, m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()) / 4); - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)), Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx)))); - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)), Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx)))); } @@ -3707,7 +3707,7 @@ void ClipperOffset::DoSquare(int j, int k) void ClipperOffset::DoMiter(int j, int k, double r) { double q = m_delta / r; - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q))); } //------------------------------------------------------------------------------ @@ -3721,14 +3721,14 @@ void ClipperOffset::DoRound(int j, int k) double X = m_normals[k].x(), Y = m_normals[k].y(), X2; for (int i = 0; i < steps; ++i) { - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[j].x() + X * m_delta), Round(m_srcPoly[j].y() + Y * m_delta))); X2 = X; X = X * m_cos - m_sin * Y; Y = X2 * m_sin + Y * m_cos; } - m_destPoly.push_back(IntPoint2d( + m_destPoly.emplace_back(IntPoint2d( Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } @@ -3996,8 +3996,8 @@ void Minkowski(const Path& poly, const Path& path, Path p; p.reserve(polyCnt); for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y())); - pp.push_back(p); + p.emplace_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y())); + pp.emplace_back(p); } else for (size_t i = 0; i < pathCnt; ++i) @@ -4005,8 +4005,8 @@ void Minkowski(const Path& poly, const Path& path, Path p; p.reserve(polyCnt); for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y())); - pp.push_back(p); + p.emplace_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y())); + pp.emplace_back(p); } solution.clear(); @@ -4016,12 +4016,12 @@ void Minkowski(const Path& poly, const Path& path, { Path quad; quad.reserve(4); - quad.push_back(pp[i % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); - quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + quad.emplace_back(pp[i % pathCnt][j % polyCnt]); + quad.emplace_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.emplace_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.emplace_back(pp[i % pathCnt][(j + 1) % polyCnt]); if (!Orientation(quad)) ReversePath(quad); - solution.push_back(quad); + solution.emplace_back(quad); } } //------------------------------------------------------------------------------ @@ -4081,7 +4081,7 @@ void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& path else if (nodetype == ntOpen) return; if (!polynode.Contour.empty() && match) - paths.push_back(polynode.Contour); + paths.emplace_back(polynode.Contour); for (int i = 0; i < polynode.ChildCount(); ++i) AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); } @@ -4093,7 +4093,7 @@ void AddPolyNodeToPaths(PolyNode&& polynode, NodeType nodetype, Paths& paths) else if (nodetype == ntOpen) return; if (!polynode.Contour.empty() && match) - paths.push_back(std::move(polynode.Contour)); + paths.emplace_back(std::move(polynode.Contour)); for (int i = 0; i < polynode.ChildCount(); ++i) AddPolyNodeToPaths(std::move(*polynode.Childs[i]), nodetype, paths); } @@ -4131,7 +4131,7 @@ void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) //Open paths are top level only, so ... for (int i = 0; i < polytree.ChildCount(); ++i) if (polytree.Childs[i]->IsOpen()) - paths.push_back(polytree.Childs[i]->Contour); + paths.emplace_back(polytree.Childs[i]->Contour); } //------------------------------------------------------------------------------ From 63ca221394acca695508dd333633b43b1a5e9744 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 18:20:38 +0200 Subject: [PATCH 41/70] douglas_peucker(): Optimized for 32bit Point types. --- src/libslic3r/MultiPoint.cpp | 44 +++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index ce54a54c00..90928d3c1c 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -106,7 +106,7 @@ bool MultiPoint::remove_duplicate_points() Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) { Points result_pts; - double tolerance_sq = tolerance * tolerance; + auto tolerance_sq = int64_t(sqr(tolerance)); if (! pts.empty()) { const Point *anchor = &pts.front(); size_t anchor_idx = 0; @@ -120,14 +120,42 @@ Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) dpStack.reserve(pts.size()); dpStack.emplace_back(floater_idx); for (;;) { - double max_dist_sq = 0.0; - size_t furthest_idx = anchor_idx; + int64_t max_dist_sq = 0; + size_t furthest_idx = anchor_idx; // find point furthest from line seg created by (anchor, floater) and note it - for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { - double dist_sq = Line::distance_to_squared(pts[i], *anchor, *floater); - if (dist_sq > max_dist_sq) { - max_dist_sq = dist_sq; - furthest_idx = i; + { + const Point a = *anchor; + const Point f = *floater; + const Vec2i64 v = (f - a).cast(); + const int64_t l2 = v.squaredNorm(); + // Make up for rounding when converting from int64_t to double. Double mantissa is just 52 bits. + if (l2 < (1 << 14)) { + for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) + if (int64_t dist_sq = (pts[i] - a).cast().squaredNorm(); dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest_idx = i; + } + } else { + const double dl2 = double(l2); + const Vec2d dv = v.cast(); + for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { + const Point p = pts[i]; + const Vec2i64 va = (p - a).template cast(); + const int64_t t = va.dot(v); + int64_t dist_sq; + if (t <= 0) { + dist_sq = va.squaredNorm(); + } else if (t >= l2) { + dist_sq = (p - f).cast().squaredNorm(); + } else { + const Vec2i64 w = ((double(t) / dl2) * dv).cast(); + dist_sq = (w - va).squaredNorm(); + } + if (dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest_idx = i; + } + } } } // remove point if less than tolerance From 2600ba71ad52d3f43d3efd244493458567a7b457 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 18:36:59 +0200 Subject: [PATCH 42/70] Fix of undefined hardware_destructive_interference_size --- src/libslic3r/TriangleMeshSlicer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 2c65702192..747f362153 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -36,6 +36,13 @@ #include #include +#ifdef __cpp_lib_hardware_interference_size + using std::hardware_destructive_interference_size; +#else + // 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ... + constexpr std::size_t hardware_destructive_interference_size = 64; +#endif + // #define SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -365,7 +372,7 @@ public: private: struct CacheLineAlignedMutex { - alignas(std::hardware_destructive_interference_size) std::mutex mutex; + alignas(hardware_destructive_interference_size) std::mutex mutex; }; std::array m_mutexes; }; From 15ccecf8858f2f5e11da62b5ccb1b25300b27538 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 18:37:24 +0200 Subject: [PATCH 43/70] TreeSupport optimization: better parallel scaling, simpler simplification --- src/libslic3r/TreeSupport.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 174e91a9cd..3452bb71c3 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -59,6 +59,8 @@ namespace Slic3r namespace FFFTreeSupport { +static constexpr const bool polygons_strictly_simple = false; + TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings, const SlicingParameters &slicing_params) : angle(mesh_group_settings.support_tree_angle), angle_slow(mesh_group_settings.support_tree_angle_slow), @@ -925,13 +927,13 @@ static std::optional> polyline_sample_next_point_at_dis ret = diff(offset(ret, step_size, ClipperLib::jtRound, scaled(0.01)), collision_trimmed()); // ensure that if many offsets are done the performance does not suffer extremely by the new vertices of jtRound. if (i % 10 == 7) - ret = polygons_simplify(ret, scaled(0.015)); + ret = polygons_simplify(ret, scaled(0.015), polygons_strictly_simple); } // offset the remainder float last_offset = distance - steps * step_size; if (last_offset > SCALED_EPSILON) ret = offset(ret, distance - steps * step_size, ClipperLib::jtRound, scaled(0.01)); - ret = polygons_simplify(ret, scaled(0.015)); + ret = polygons_simplify(ret, scaled(0.015), polygons_strictly_simple); if (do_final_difference) ret = diff(ret, collision_trimmed()); @@ -1797,7 +1799,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di } if (settings.no_error && settings.move) // as ClipperLib::jtRound has to be used for offsets this simplify is VERY important for performance. - polygons_simplify(increased, scaled(0.025)); + polygons_simplify(increased, scaled(0.025), polygons_strictly_simple); } else // if no movement is done the areas keep parent area as no move == offset(0) increased = parent.influence_area; @@ -1821,6 +1823,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong non gracious value on layer " << layer_idx - 1 << " targeting " << current_elem.target_height << " with radius " << radius; } else + // Cannot route to gracious areas. Push the tree away from object and route it down anyways. to_model_data = safe_union(diff_clipped(increased, volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance))); } } @@ -1966,7 +1969,7 @@ static void increase_areas_one_layer( { using AvoidanceType = TreeModelVolumes::AvoidanceType; - tbb::parallel_for(tbb::blocked_range(0, merging_areas.size()), + tbb::parallel_for(tbb::blocked_range(0, merging_areas.size(), 1), [&](const tbb::blocked_range &range) { for (size_t merging_area_idx = range.begin(); merging_area_idx < range.end(); ++ merging_area_idx) { SupportElementMerging &merging_area = merging_areas[merging_area_idx]; @@ -2209,9 +2212,10 @@ static void increase_areas_one_layer( // A point can be set on the top most tip layer (maybe more if it should not move for a few layers). parent.state.result_on_layer_reset(); } + throw_on_cancel(); } - }); + }, tbb::simple_partitioner()); } [[nodiscard]] static SupportElementState merge_support_element_states( @@ -3325,7 +3329,7 @@ static void finalize_interface_and_support_areas( base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50); //smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : // simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution. - base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled(0.03), double(config.resolution))); + base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled(0.03), double(config.resolution)), polygons_strictly_simple); } if (! support_roof_polygons.empty() && ! base_layer_polygons.empty()) { @@ -4270,7 +4274,7 @@ static std::vector draw_branches( const SlicingParameters &slicing_params = print_object.slicing_parameters(); MeshSlicingParams mesh_slicing_params; mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive; - tbb::parallel_for(tbb::blocked_range(0, trees.size()), + tbb::parallel_for(tbb::blocked_range(0, trees.size(), 1), [&trees, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { indexed_triangle_set partial_mesh; std::vector slice_z; @@ -4322,9 +4326,9 @@ static std::vector draw_branches( tree.first_layer_id = new_begin; } } - }); + }, tbb::simple_partitioner()); - tbb::parallel_for(tbb::blocked_range(0, trees.size()), + tbb::parallel_for(tbb::blocked_range(0, trees.size(), 1), [&trees, &throw_on_cancel](const tbb::blocked_range &range) { for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) { Tree &tree = trees[tree_id]; @@ -4335,7 +4339,7 @@ static std::vector draw_branches( } throw_on_cancel(); } - }); + }, tbb::simple_partitioner()); size_t num_layers = 0; for (Tree &tree : trees) @@ -4356,14 +4360,14 @@ static std::vector draw_branches( } std::vector support_layer_storage(move_bounds.size()); - tbb::parallel_for(tbb::blocked_range(0, std::min(move_bounds.size(), slices.size())), + tbb::parallel_for(tbb::blocked_range(0, std::min(move_bounds.size(), slices.size()), 1), [&slices, &support_layer_storage, &throw_on_cancel](const tbb::blocked_range &range) { for (size_t slice_id = range.begin(); slice_id < range.end(); ++ slice_id) { Slice &slice = slices[slice_id]; support_layer_storage[slice_id] = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons); throw_on_cancel(); } - }); + }, tbb::simple_partitioner()); //FIXME simplify! return support_layer_storage; From 0c6f2261a3518ef2ae5337e34179632c503d4f48 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 18:45:07 +0200 Subject: [PATCH 44/70] Tree Supports: Extend bottoms of trees downwards to the object surface if those supports are not sitting on a flat surface. --- src/libslic3r/TreeSupport.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 3452bb71c3..820b60d496 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -4275,7 +4275,7 @@ static std::vector draw_branches( MeshSlicingParams mesh_slicing_params; mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive; tbb::parallel_for(tbb::blocked_range(0, trees.size(), 1), - [&trees, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { + [&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { indexed_triangle_set partial_mesh; std::vector slice_z; for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) { @@ -4297,7 +4297,23 @@ static std::vector draw_branches( slice_z.emplace_back(float(0.5 * (bottom_z + print_z))); } std::vector slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel); - size_t num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); + size_t num_empty = 0; + + if (layer_begin > 0 && branch.has_root && ! branch.path.front()->state.to_model_gracious && ! slices.front().empty()) { + // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something. + std::vector bottom_extra_slices; + Polygons rest_support; + for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= 0; -- layer_idx) { + rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false)); + if (area(rest_support) < tiny_area_threshold) + break; + bottom_extra_slices.emplace_back(rest_support); + } + layer_begin -= LayerIndex(bottom_extra_slices.size()); + slices.insert(slices.begin(), std::make_move_iterator(bottom_extra_slices.rbegin()), std::make_move_iterator(bottom_extra_slices.rend())); + } else + num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); + layer_begin += LayerIndex(num_empty); for (; slices.back().empty(); -- layer_end); LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin); From d0f38cd0b4c65e0b2b2e91d9df3669fc3f65bdd8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 19:26:33 +0200 Subject: [PATCH 45/70] macOS clang is buggy, it does not implement __cpp_lib_hardware_interference_size correctly. --- src/libslic3r/TriangleMeshSlicer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 747f362153..da696e1ecb 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -36,7 +36,7 @@ #include #include -#ifdef __cpp_lib_hardware_interference_size +#if defined(__cpp_lib_hardware_interference_size) && ! defined(__APPLE__) using std::hardware_destructive_interference_size; #else // 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ... From af6c27b8613959319bbb66d1f066e55c2e231b1e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 19:50:12 +0200 Subject: [PATCH 46/70] douglas_peucker(): fix after 63ca221394acca695508dd333633b43b1a5e9744 --- src/libslic3r/MultiPoint.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 90928d3c1c..fb4727abe5 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -124,12 +124,10 @@ Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) size_t furthest_idx = anchor_idx; // find point furthest from line seg created by (anchor, floater) and note it { - const Point a = *anchor; - const Point f = *floater; + const Point a = *anchor; + const Point f = *floater; const Vec2i64 v = (f - a).cast(); - const int64_t l2 = v.squaredNorm(); - // Make up for rounding when converting from int64_t to double. Double mantissa is just 52 bits. - if (l2 < (1 << 14)) { + if (const int64_t l2 = v.squaredNorm(); l2 == 0) { for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) if (int64_t dist_sq = (pts[i] - a).cast().squaredNorm(); dist_sq > max_dist_sq) { max_dist_sq = dist_sq; From 6831b7094a47a1dfcc6a283b9156f3026adf0b0c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 3 Apr 2023 16:02:26 +0200 Subject: [PATCH 47/70] Trying to fix transformation assembly problem for arrange polyogon --- src/libslic3r/Model.cpp | 13 +++++++------ src/libslic3r/Model.hpp | 13 ++++++++++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 6c350e7b42..f0ad7e1e05 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2406,11 +2406,12 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const { // static const double SIMPLIFY_TOLERANCE_MM = 0.1; - Vec3d rotation = get_rotation(); - rotation.z() = 0.; - Transform3d trafo_instance = Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation, get_scaling_factor(), get_mirror()); +// Vec3d rotation = get_rotation(); +// rotation.z() = 0.; +// Transform3d trafo_instance = Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation, get_scaling_factor(), get_mirror()); - Polygon p = get_object()->convex_hull_2d(trafo_instance); + + Polygon p = get_object()->convex_hull_2d(this->get_matrix()); // if (!p.points.empty()) { // Polygons pp{p}; @@ -2420,8 +2421,8 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const arrangement::ArrangePolygon ret; ret.poly.contour = std::move(p); - ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}; - ret.rotation = get_rotation(Z); + ret.translation = Vec2crd::Zero(); //Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}; + ret.rotation = 0.; return ret; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 55aceeeb2f..d29ce3525b 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -1170,9 +1170,16 @@ public: void apply_arrange_result(const Vec2d& offs, double rotation) { // write the transformation data into the model instance - set_rotation(Z, rotation); - set_offset(X, unscale(offs(X))); - set_offset(Y, unscale(offs(Y))); +// set_rotation(Z, rotation); +// set_offset(X, unscale(offs(X))); +// set_offset(Y, unscale(offs(Y))); + auto trafo = get_transformation().get_matrix(); + trafo.translate(to_3d(unscaled(offs), 0.)); + trafo.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ())); + m_transformation.set_matrix(trafo); + +// set_rotation(Z, get_rotation().z() + rotation); +// set_offset(get_offset() + to_3d(unscaled(offs), 0.)); this->object->invalidate_bounding_box(); } From 362267431be2d271ef0ccd13cf4abf673cf9a51f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 4 Apr 2023 14:39:45 +0200 Subject: [PATCH 48/70] Fix transformation assembly problem for arrange polyogon --- src/libslic3r/Model.cpp | 19 +++++++++++++------ src/libslic3r/Model.hpp | 16 +--------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index f0ad7e1e05..837f32479c 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2405,11 +2405,6 @@ void ModelInstance::transform_polygon(Polygon* polygon) const arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const { // static const double SIMPLIFY_TOLERANCE_MM = 0.1; - -// Vec3d rotation = get_rotation(); -// rotation.z() = 0.; -// Transform3d trafo_instance = Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation, get_scaling_factor(), get_mirror()); - Polygon p = get_object()->convex_hull_2d(this->get_matrix()); @@ -2421,12 +2416,24 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const arrangement::ArrangePolygon ret; ret.poly.contour = std::move(p); - ret.translation = Vec2crd::Zero(); //Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}; + ret.translation = Vec2crd::Zero(); ret.rotation = 0.; return ret; } +void ModelInstance::apply_arrange_result(const Vec2d &offs, double rotation) +{ + // write the transformation data into the model instance + auto trafo = get_transformation().get_matrix(); + auto tr = Transform3d::Identity(); + tr.translate(to_3d(unscaled(offs), 0.)); + trafo = tr * Eigen::AngleAxisd(rotation, Vec3d::UnitZ()) * trafo; + m_transformation.set_matrix(trafo); + + this->object->invalidate_bounding_box(); +} + indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const { TriangleSelector selector(mv.mesh()); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index d29ce3525b..ea22b968d5 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -1167,21 +1167,7 @@ public: arrangement::ArrangePolygon get_arrange_polygon() const; // Apply the arrange result on the ModelInstance - void apply_arrange_result(const Vec2d& offs, double rotation) - { - // write the transformation data into the model instance -// set_rotation(Z, rotation); -// set_offset(X, unscale(offs(X))); -// set_offset(Y, unscale(offs(Y))); - auto trafo = get_transformation().get_matrix(); - trafo.translate(to_3d(unscaled(offs), 0.)); - trafo.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ())); - m_transformation.set_matrix(trafo); - -// set_rotation(Z, get_rotation().z() + rotation); -// set_offset(get_offset() + to_3d(unscaled(offs), 0.)); - this->object->invalidate_bounding_box(); - } + void apply_arrange_result(const Vec2d& offs, double rotation); protected: friend class Print; From 49fbf4ccceaf71ec130f49e9a9fe2c3d4208ccb3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 3 May 2023 09:45:11 +0200 Subject: [PATCH 49/70] Avoid garbage error message when generating sla preview --- src/libslic3r/SLAPrintSteps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index a38b17da1f..33e2a6e20f 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -305,7 +305,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st bench.stop(); - if (!m.empty()) + if (!po.m_preview_meshes[step]->empty()) BOOST_LOG_TRIVIAL(trace) << "Preview gen took: " << bench.getElapsedSec(); else BOOST_LOG_TRIVIAL(error) << "Preview failed!"; From 234956dfda61d9669d7a16b3957cd3de277c525c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 3 May 2023 14:26:06 +0200 Subject: [PATCH 50/70] Fix for SPE-1686 : Bug with instance rotation around X or Y Steps to repro: 1. Add object 2. Increase instances 3. Select some instance 4. Rotate around X or Y 5. After MouseUp: OSX -> hard crash MSW and Linux -> object and instance are selected at a same time --- src/slic3r/GUI/GLCanvas3D.cpp | 30 +++++++++++++++++++++++++----- src/slic3r/GUI/GUI_ObjectList.cpp | 24 +++++++++++------------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index dbb2106802..8116e8843f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3641,6 +3641,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) } // Fixes flying instances + std::set obj_idx_for_update_info_items; for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; const double shift_z = m->get_instance_min_z(i.second); @@ -3649,8 +3650,11 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); } - wxGetApp().obj_list()->update_info_items(static_cast(i.first)); + obj_idx_for_update_info_items.emplace(i.first); } + //update sinking information in ObjectList + for (int id : obj_idx_for_update_info_items) + wxGetApp().obj_list()->update_info_items(static_cast(id)); // if the selection is not valid to allow for layer editing after the move, we need to turn off the tool if it is running // similar to void Plater::priv::selection_changed() @@ -3728,6 +3732,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) } // Fixes sinking/flying instances + std::set obj_idx_for_update_info_items; for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; const double shift_z = m->get_instance_min_z(i.second); @@ -3738,8 +3743,11 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) m->translate_instance(i.second, shift); } - wxGetApp().obj_list()->update_info_items(static_cast(i.first)); + obj_idx_for_update_info_items.emplace(i.first); } + //update sinking information in ObjectList + for (int id : obj_idx_for_update_info_items) + wxGetApp().obj_list()->update_info_items(static_cast(id)); if (!done.empty()) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); @@ -3797,6 +3805,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) } // Fixes sinking/flying instances + std::set obj_idx_for_update_info_items; for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; const double shift_z = m->get_instance_min_z(i.second); @@ -3806,8 +3815,11 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); } - wxGetApp().obj_list()->update_info_items(static_cast(i.first)); + obj_idx_for_update_info_items.emplace(i.first); } + //update sinking information in ObjectList + for (int id : obj_idx_for_update_info_items) + wxGetApp().obj_list()->update_info_items(static_cast(id)); if (!done.empty()) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED)); @@ -3860,6 +3872,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) } // Fixes sinking/flying instances + std::set obj_idx_for_update_info_items; for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; double shift_z = m->get_instance_min_z(i.second); @@ -3869,8 +3882,11 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); } - wxGetApp().obj_list()->update_info_items(static_cast(i.first)); + obj_idx_for_update_info_items.emplace(i.first); } + //update sinking information in ObjectList + for (int id : obj_idx_for_update_info_items) + wxGetApp().obj_list()->update_info_items(static_cast(id)); post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); @@ -3922,6 +3938,7 @@ void GLCanvas3D::do_reset_skew(const std::string& snapshot_type) } // Fixes sinking/flying instances + std::set obj_idx_for_update_info_items; for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; double shift_z = m->get_instance_min_z(i.second); @@ -3931,8 +3948,11 @@ void GLCanvas3D::do_reset_skew(const std::string& snapshot_type) m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); } - wxGetApp().obj_list()->update_info_items(static_cast(i.first)); + obj_idx_for_update_info_items.emplace(i.first); } + //update sinking information in ObjectList + for (int id : obj_idx_for_update_info_items) + wxGetApp().obj_list()->update_info_items(static_cast(id)); post_event(SimpleEvent(EVT_GLCANVAS_RESET_SKEW)); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 85d12bec9d..ffed1eef68 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2994,21 +2994,19 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio wxGetApp().notification_manager()->push_updated_item_info_notification(type); } else if (shows && ! should_show) { - if (!selections) + if (!selections && IsSelected(item)) { Unselect(item); - m_objects_model->Delete(item); - if (selections) { - if (selections->Index(item) != wxNOT_FOUND) { - // If info item was deleted from the list, - // it's need to be deleted from selection array, if it was there - selections->Remove(item); - // Select item_obj, if info_item doesn't exist for item anymore, but was selected - if (selections->Index(item_obj) == wxNOT_FOUND) - selections->Add(item_obj); - } - } - else Select(item_obj); + } + m_objects_model->Delete(item); + if (selections && selections->Index(item) != wxNOT_FOUND) { + // If info item was deleted from the list, + // it's need to be deleted from selection array, if it was there + selections->Remove(item); + // Select item_obj, if info_item doesn't exist for item anymore, but was selected + if (selections->Index(item_obj) == wxNOT_FOUND) + selections->Add(item_obj); + } } } } From a039391131fc6cecbfbca406bd3a2627b93814cc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 4 May 2023 13:56:05 +0200 Subject: [PATCH 51/70] Fixed rendering of horizontal ellipsis in ImGui controls --- src/slic3r/GUI/ImGuiWrapper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 0754c35cc9..f2e6b02878 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1620,6 +1620,8 @@ void ImGuiWrapper::init_font(bool compress) ImFontGlyphRangesBuilder builder; builder.AddRanges(m_glyph_ranges); + builder.AddChar(ImWchar(0x2026)); // … + if (m_font_cjk) { // This is a temporary fix of https://github.com/prusa3d/PrusaSlicer/issues/8171. The translation // contains characters not in the ImGui ranges for simplified Chinese. For now, just add them manually. From 34015349c177feef289dc3f480f5d52cc69ddecf Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 4 May 2023 14:59:50 +0200 Subject: [PATCH 52/70] Fix a rounded extrusion model when the new width is smaller than layer height - the code used radius in place of diameter --- src/libslic3r/Flow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 1084e6f102..bc3a2d7775 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -176,7 +176,7 @@ Flow Flow::with_cross_section(float area_new) const return this->with_width(width_new); } else { // Create a rounded extrusion. - auto dmr = float(sqrt(area_new / M_PI)); + auto dmr = 2.0 * float(sqrt(area_new / M_PI)); return Flow(dmr, dmr, m_spacing, m_nozzle_diameter, false); } } else From 8593ad1f80011994b33a130c2d634cb83acda5f6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 4 May 2023 15:26:41 +0200 Subject: [PATCH 53/70] Organic supports improvements: Removing collisions with trees, limiting how far tree bottoms at slanted surfaces could be extended down below their last full circle position. Placable areas are now calculated sitting on slightly inflated top surface to indicate support of tree bottoms at slanted surfaces. --- src/libslic3r/ClipperUtils.cpp | 2 + src/libslic3r/ClipperUtils.hpp | 3 + src/libslic3r/TreeModelVolumes.cpp | 17 ++-- src/libslic3r/TreeModelVolumes.hpp | 3 +- src/libslic3r/TreeSupport.cpp | 156 ++++++++++++++++++++++------- src/libslic3r/TreeSupport.hpp | 12 +++ 6 files changed, 147 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index d10e14cc53..85ef53c888 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -685,6 +685,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c { return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); } +Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) + { return intersection(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index ab967fd8fb..774e9cb42f 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -453,6 +453,9 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox(). +// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon. +Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index 3c82b5c6c2..10840d1b5a 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -426,7 +426,7 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L return (*result).get(); if (m_precalculated) { BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; - tree_supports_show_error("Not precalculated Placeable areas requested."sv, false); + tree_supports_show_error(format("Not precalculated Placeable areas requested, radius %1%, layer %2%", radius, layer_idx), false); } if (orig_radius == 0) // Placable areas for radius 0 are calculated in the general collision code. @@ -597,16 +597,19 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex // 3) Optionally calculate placables. if (calculate_placable) { // Now calculate the placable areas. - tbb::parallel_for(tbb::blocked_range(std::max(data.idx_begin, 1), data.idx_end), - [&collision_areas_offsetted, &anti_overhang = m_anti_overhang, processing_last_mesh, - min_resolution = m_min_resolution, &data_placeable, &throw_on_cancel] + tbb::parallel_for(tbb::blocked_range(std::max(z_distance_bottom_layers + 1, data.idx_begin), data.idx_end), + [&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh, + min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel] (const tbb::blocked_range& range) { for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) { - LayerIndex layer_idx_below = layer_idx - 1; + LayerIndex layer_idx_below = layer_idx - z_distance_bottom_layers - 1; assert(layer_idx_below >= 0); const Polygons ¤t = collision_areas_offsetted[layer_idx]; - const Polygons &below = collision_areas_offsetted[layer_idx_below]; - Polygons placable = diff(below, layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current); + const Polygons &below = outlines[layer_idx_below]; + Polygons placable = diff( + // Inflate the surface to sit on by the separation distance to increase chance of a support being placed on a sloped surface. + offset(below, xy_distance), + layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current); auto &dst = data_placeable[layer_idx]; if (processing_last_mesh) { if (! dst.empty()) diff --git a/src/libslic3r/TreeModelVolumes.hpp b/src/libslic3r/TreeModelVolumes.hpp index 139b12328c..659baf1ff6 100644 --- a/src/libslic3r/TreeModelVolumes.hpp +++ b/src/libslic3r/TreeModelVolumes.hpp @@ -215,6 +215,7 @@ public: void clear() { this->clear_all_but_object_collision(); m_collision_cache.clear(); + m_placeable_areas_cache.clear(); } void clear_all_but_object_collision() { //m_collision_cache.clear_all_but_radius0(); @@ -223,7 +224,7 @@ public: m_avoidance_cache_slow.clear(); m_avoidance_cache_to_model.clear(); m_avoidance_cache_to_model_slow.clear(); - m_placeable_areas_cache.clear(); + m_placeable_areas_cache.clear_all_but_radius0(); m_avoidance_cache_holefree.clear(); m_avoidance_cache_holefree_to_model.clear(); m_wall_restrictions_cache.clear(); diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 820b60d496..9a7b203c60 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -299,7 +299,7 @@ static bool inline g_showed_critical_error = false; static bool inline g_showed_performance_warning = false; void tree_supports_show_error(std::string_view message, bool critical) { // todo Remove! ONLY FOR PUBLIC BETA!! - +// printf("Error: %s, critical: %d\n", message.data(), int(critical)); #ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32 static bool showed_critical = false; static bool showed_performance = false; @@ -2158,6 +2158,10 @@ static void increase_areas_one_layer( " Distance to top: " << parent.state.distance_to_top << " Elephant foot increases " << parent.state.elephant_foot_increases << " use_min_xy_dist " << parent.state.use_min_xy_dist << " to buildplate " << parent.state.to_buildplate << " gracious " << parent.state.to_model_gracious << " safe " << parent.state.can_use_safe_radius << " until move " << parent.state.dont_move_until; tree_supports_show_error("Potentially lost branch!"sv, true); +#ifdef TREE_SUPPORTS_TRACK_LOST + if (result) + result->lost = true; +#endif // TREE_SUPPORTS_TRACK_LOST } else result = increase_single_area(volumes, config, settings, layer_idx, parent, settings.increase_speed == slow_speed ? offset_slow : offset_fast, to_bp_data, to_model_data, inc_wo_collision, 0, mergelayer); @@ -2211,6 +2215,9 @@ static void increase_areas_one_layer( // But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set. // A point can be set on the top most tip layer (maybe more if it should not move for a few layers). parent.state.result_on_layer_reset(); +#ifdef TREE_SUPPORTS_TRACK_LOST + parent.state.verylost = true; +#endif // TREE_SUPPORTS_TRACK_LOST } throw_on_cancel(); @@ -4234,26 +4241,35 @@ static std::vector draw_branches( branch.path.emplace_back(&start_element); // Traverse each branch until it branches again. SupportElement &first_parent = layer_above[start_element.parents[parent_idx]]; + assert(! first_parent.state.marked); assert(branch.path.back()->state.layer_idx + 1 == first_parent.state.layer_idx); branch.path.emplace_back(&first_parent); if (first_parent.parents.size() < 2) first_parent.state.marked = true; SupportElement *next_branch = nullptr; - if (first_parent.parents.size() == 1) + if (first_parent.parents.size() == 1) { for (SupportElement *parent = &first_parent;;) { + assert(parent->state.marked); SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()]; + assert(! next_parent.state.marked); assert(branch.path.back()->state.layer_idx + 1 == next_parent.state.layer_idx); branch.path.emplace_back(&next_parent); if (next_parent.parents.size() > 1) { + // Branching point was reached. next_branch = &next_parent; break; } next_parent.state.marked = true; if (next_parent.parents.size() == 0) + // Tip is reached. break; parent = &next_parent; } + } else if (first_parent.parents.size() > 1) + // Branching point was reached. + next_branch = &first_parent; assert(branch.path.size() >= 2); + assert(next_branch == nullptr || ! next_branch->state.marked); branch.has_root = root; branch.has_tip = ! next_branch; out.branches.emplace_back(std::move(branch)); @@ -4263,13 +4279,37 @@ static std::vector draw_branches( } }; - for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) - for (SupportElement &start_element : move_bounds[layer_idx]) - if (! start_element.state.marked && ! start_element.parents.empty()) { + for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) { +// int ielement; + for (SupportElement& start_element : move_bounds[layer_idx]) { + if (!start_element.state.marked && !start_element.parents.empty()) { +#if 0 + int found = 0; + if (layer_idx > 0) { + for (auto& el : move_bounds[layer_idx - 1]) { + for (auto iparent : el.parents) + if (iparent == ielement) + ++found; + } + if (found != 0) + printf("Found: %d\n", found); + } +#endif trees.push_back({}); TreeVisitor::visit_recursive(move_bounds, start_element, trees.back()); - assert(! trees.back().branches.empty()); + assert(!trees.back().branches.empty()); + //FIXME debugging +#if 0 + if (start_element.state.lost) { + } + else if (start_element.state.verylost) { + } else + trees.pop_back(); +#endif } +// ++ ielement; + } + } const SlicingParameters &slicing_params = print_object.slicing_parameters(); MeshSlicingParams mesh_slicing_params; @@ -4297,49 +4337,89 @@ static std::vector draw_branches( slice_z.emplace_back(float(0.5 * (bottom_z + print_z))); } std::vector slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel); - size_t num_empty = 0; + //FIXME parallelize? + for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++ i) + slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); //FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist); + size_t num_empty = 0; if (layer_begin > 0 && branch.has_root && ! branch.path.front()->state.to_model_gracious && ! slices.front().empty()) { // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something. - std::vector bottom_extra_slices; - Polygons rest_support; - for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= 0; -- layer_idx) { + struct BottomExtraSlice { + Polygons polygons; + Polygons supported; + double area; + double supported_area; + }; + std::vector bottom_extra_slices; + Polygons rest_support; + coord_t bottom_radius = config.getRadius(branch.path.front()->state); + // Don't propagate further than 1.5 * bottom radius. + //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height; + LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height; + LayerIndex layer_bottommost = std::max(0, layer_begin - layers_propagate_max); + // Only propagate until the rest area is smaller than this threshold. + double support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius)); + // Only propagate until the rest area is smaller than this threshold. + double support_area_min = 0.1 * M_PI * sqr(double(config.min_radius)); + for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) { rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false)); - if (area(rest_support) < tiny_area_threshold) + double rest_support_area = area(rest_support); + if (rest_support_area < support_area_stop) + // Don't propagate a fraction of the tree contact surface. break; - bottom_extra_slices.emplace_back(rest_support); + // Measure how much the rest_support is actually supported. + /* + Polygons supported = intersection_clipped(rest_support, volumes.getPlaceableAreas(0, layer_idx, []{})); + double supported_area = area(supported); + printf("Supported area: %d, %lf\n", layer_idx, supported_area); + */ + Polygons supported; + double supported_area; + bottom_extra_slices.push_back({ rest_support, std::move(supported), rest_support_area, supported_area }); } + // Now remove those bottom slices that are not supported at all. + while (! bottom_extra_slices.empty() && + area(intersection_clipped(bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}))) < support_area_min) + bottom_extra_slices.pop_back(); layer_begin -= LayerIndex(bottom_extra_slices.size()); - slices.insert(slices.begin(), std::make_move_iterator(bottom_extra_slices.rbegin()), std::make_move_iterator(bottom_extra_slices.rend())); + slices.insert(slices.begin(), bottom_extra_slices.size(), {}); + size_t i = 0; + for (auto it = bottom_extra_slices.rbegin(); it != bottom_extra_slices.rend(); ++it, ++i) + slices[i] = std::move(it->polygons); } else num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); layer_begin += LayerIndex(num_empty); - for (; slices.back().empty(); -- layer_end); - LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin); - LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end); - size_t new_size = size_t(new_end - new_begin); - if (tree.first_layer_id == -1) { - } else if (tree.slices.capacity() < new_size) { - std::vector new_slices; - new_slices.reserve(new_size); - if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) - new_slices.insert(new_slices.end(), dif, {}); - append(new_slices, std::move(tree.slices)); - tree.slices.swap(new_slices); - } else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) - tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {}); - tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {}); - layer_begin -= LayerIndex(num_empty); - for (LayerIndex i = layer_begin; i != layer_end; ++ i) - if (Polygons &src = slices[i - layer_begin]; ! src.empty()) { - Slice &dst = tree.slices[i - new_begin]; - if (++ dst.num_branches > 1) - append(dst.polygons, std::move(src)); - else - dst.polygons = std::move(std::move(src)); - } - tree.first_layer_id = new_begin; + while (! slices.empty() && slices.back().empty()) { + slices.pop_back(); + -- layer_end; + } + if (layer_begin < layer_end) { + LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin); + LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end); + size_t new_size = size_t(new_end - new_begin); + if (tree.first_layer_id == -1) { + } else if (tree.slices.capacity() < new_size) { + std::vector new_slices; + new_slices.reserve(new_size); + if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) + new_slices.insert(new_slices.end(), dif, {}); + append(new_slices, std::move(tree.slices)); + tree.slices.swap(new_slices); + } else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) + tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {}); + tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {}); + layer_begin -= LayerIndex(num_empty); + for (LayerIndex i = layer_begin; i != layer_end; ++ i) + if (Polygons &src = slices[i - layer_begin]; ! src.empty()) { + Slice &dst = tree.slices[i - new_begin]; + if (++ dst.num_branches > 1) + append(dst.polygons, std::move(src)); + else + dst.polygons = std::move(std::move(src)); + } + tree.first_layer_id = new_begin; + } } } }, tbb::simple_partitioner()); diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index 070903096f..2c87a132d6 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -93,6 +93,8 @@ struct AreaIncreaseSettings struct TreeSupportSettings; +// #define TREE_SUPPORTS_TRACK_LOST + // C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided. struct SupportElementStateBits { SupportElementStateBits() : @@ -102,6 +104,10 @@ struct SupportElementStateBits { supports_roof(false), can_use_safe_radius(false), skip_ovalisation(false), +#ifdef TREE_SUPPORTS_TRACK_LOST + lost(false), + verylost(false), +#endif // TREE_SUPPORTS_TRACK_LOST deleted(false), marked(false) {} @@ -136,6 +142,12 @@ struct SupportElementStateBits { */ bool skip_ovalisation : 1; +#ifdef TREE_SUPPORTS_TRACK_LOST + // Likely a lost branch, debugging information. + bool lost : 1; + bool verylost : 1; +#endif // TREE_SUPPORTS_TRACK_LOST + // Not valid anymore, to be deleted. bool deleted : 1; From 3b9037b44248ca19a49545ed31933c1b4c31bfe4 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 4 May 2023 15:27:09 +0200 Subject: [PATCH 54/70] nlopt in RelWithDebInfo now compiles as release. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 66b9a777b0..49fe3437f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -546,6 +546,7 @@ foreach(po_file ${L10N_PO_FILES}) endforeach() find_package(NLopt 1.4 REQUIRED) +slic3r_remap_configs(NLopt::nlopt RelWithDebInfo Release) if(SLIC3R_STATIC) set(OPENVDB_USE_STATIC_LIBS ON) From 6d0ceeb8861bfda4c3d84ffd1ea1c3fc700d406d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 12:59:20 +0200 Subject: [PATCH 55/70] ClipperLib: Further optimization of memory allocation using scalable_allocator. ClipperLib: SimplifyPolygon() - changed default winding number to positive, added strictly_simple parameter. ClipperUtlis simplify_polygons() - removed "remove_collinear" parameter --- src/clipper/clipper.cpp | 206 +++++++++++------------- src/clipper/clipper.hpp | 43 +++-- src/libslic3r/Arachne/WallToolPaths.cpp | 4 +- src/libslic3r/ClipperUtils.cpp | 25 ++- src/libslic3r/ClipperUtils.hpp | 4 +- 5 files changed, 137 insertions(+), 145 deletions(-) diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index c775a3226c..5da79d3a16 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -73,25 +73,6 @@ static int const Skip = -2; //edge that would otherwise close a path #define TOLERANCE (1.0e-20) #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) -// Output polygon. -struct OutRec { - int Idx; - bool IsHole; - bool IsOpen; - //The 'FirstLeft' field points to another OutRec that contains or is the - //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is - //parsed left from the current edge (owning OutRec) until the owner OutRec - //is found. This field simplifies sorting the polygons into a tree structure - //which reflects the parent/child relationships of all polygons. - //This field should be renamed Parent, and will be later. - OutRec *FirstLeft; - // Used only by void Clipper::BuildResult2(PolyTree& polytree) - PolyNode *PolyNd; - // Linked list of output points, dynamically allocated. - OutPt *Pts; - OutPt *BottomPt; -}; - //------------------------------------------------------------------------------ inline IntPoint IntPoint2d(cInt x, cInt y) @@ -1061,8 +1042,7 @@ IntRect ClipperBase::GetBounds() Clipper::Clipper(int initOptions) : ClipperBase(), m_OutPtsFree(nullptr), - m_OutPtsChunkSize(32), - m_OutPtsChunkLast(32), + m_OutPtsChunkLast(m_OutPtsChunkSize), m_ActiveEdges(nullptr), m_SortedEdges(nullptr) { @@ -1153,23 +1133,23 @@ bool Clipper::ExecuteInternal() //FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers? //FIXME Vojtech: The area is calculated with floats, it may not be numerically stable! { - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); + for (OutRec &outRec : m_PolyOuts) + if (outRec.Pts && !outRec.IsOpen && (outRec.IsHole ^ m_ReverseOutput) == (Area(outRec) > 0)) + ReversePolyPtLinks(outRec.Pts); } JoinCommonEdges(); //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() { - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts) { - if (outRec->IsOpen) + for (OutRec &outRec : m_PolyOuts) + if (outRec.Pts) { + if (outRec.IsOpen) // Removes duplicate points. - FixupOutPolyline(*outRec); + FixupOutPolyline(outRec); else // Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex. - FixupOutPolygon(*outRec); + FixupOutPolygon(outRec); } } // For each polygon, search for exactly duplicate non-successive points. @@ -1194,22 +1174,18 @@ OutPt* Clipper::AllocateOutPt() m_OutPtsFree = pt->Next; } else if (m_OutPtsChunkLast < m_OutPtsChunkSize) { // Get a point from the last chunk. - pt = m_OutPts.back() + (m_OutPtsChunkLast ++); + pt = &m_OutPts.back()[m_OutPtsChunkLast ++]; } else { // The last chunk is full. Allocate a new one. - m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]); + m_OutPts.push_back({}); m_OutPtsChunkLast = 1; - pt = m_OutPts.back(); + pt = &m_OutPts.back().front(); } return pt; } void Clipper::DisposeAllOutRecs() { - for (OutPt *pts : m_OutPts) - delete[] pts; - for (OutRec *rec : m_PolyOuts) - delete rec; m_OutPts.clear(); m_OutPtsFree = nullptr; m_OutPtsChunkLast = m_OutPtsChunkSize; @@ -1832,7 +1808,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) } //------------------------------------------------------------------------------ -void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) { bool IsHole = false; TEdge *e2 = e->PrevInAEL; @@ -1842,7 +1818,7 @@ void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const { IsHole = !IsHole; if (! outrec->FirstLeft) - outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; + outrec->FirstLeft = &m_PolyOuts[e2->OutIdx]; } e2 = e2->PrevInAEL; } @@ -1883,18 +1859,18 @@ bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) OutRec* Clipper::GetOutRec(int Idx) { - OutRec* outrec = m_PolyOuts[Idx]; - while (outrec != m_PolyOuts[outrec->Idx]) - outrec = m_PolyOuts[outrec->Idx]; + OutRec* outrec = &m_PolyOuts[Idx]; + while (outrec != &m_PolyOuts[outrec->Idx]) + outrec = &m_PolyOuts[outrec->Idx]; return outrec; } //------------------------------------------------------------------------------ -void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) { //get the start and ends of both output polygons ... - OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; - OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + OutRec *outRec1 = &m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = &m_PolyOuts[e2->OutIdx]; OutRec *holeStateRec; if (Param1RightOfParam2(outRec1, outRec2)) @@ -1991,16 +1967,16 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const OutRec* Clipper::CreateOutRec() { - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size()-1; - return result; + m_PolyOuts.push_back({}); + OutRec &result = m_PolyOuts.back(); + result.IsHole = false; + result.IsOpen = false; + result.FirstLeft = 0; + result.Pts = 0; + result.BottomPt = 0; + result.PolyNd = 0; + result.Idx = (int)m_PolyOuts.size()-1; + return &result; } //------------------------------------------------------------------------------ @@ -2022,7 +1998,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) return newOp; } else { - OutRec *outRec = m_PolyOuts[e->OutIdx]; + OutRec *outRec = &m_PolyOuts[e->OutIdx]; //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' OutPt* op = outRec->Pts; @@ -2045,7 +2021,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) OutPt* Clipper::GetLastOutPt(TEdge *e) { - OutRec *outRec = m_PolyOuts[e->OutIdx]; + OutRec *outRec = &m_PolyOuts[e->OutIdx]; if (e->Side == esLeft) return outRec->Pts; else @@ -2216,7 +2192,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { Direction dir; cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen); + bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx].IsOpen); GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); @@ -2778,12 +2754,12 @@ int PointCount(OutPt *Pts) void Clipper::BuildResult(Paths &polys) { polys.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - assert(! outRec->IsOpen); - if (!outRec->Pts) continue; + assert(! outRec.IsOpen); + if (!outRec.Pts) continue; Path pg; - OutPt* p = outRec->Pts->Prev; + OutPt* p = outRec.Pts->Prev; int cnt = PointCount(p); if (cnt < 2) continue; pg.reserve(cnt); @@ -2802,31 +2778,31 @@ void Clipper::BuildResult2(PolyTree& polytree) polytree.Clear(); polytree.AllNodes.reserve(m_PolyOuts.size()); //add each output polygon/contour to polytree ... - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) + int cnt = PointCount(outRec.Pts); + if ((outRec.IsOpen && cnt < 2) || (!outRec.IsOpen && cnt < 3)) // Ignore an invalid output loop or a polyline. continue; //skip OutRecs that (a) contain outermost polygons or //(b) already have the correct owner/child linkage ... - if (outRec->FirstLeft && - (outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) { - OutRec* orfl = outRec->FirstLeft; - while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts)) + if (outRec.FirstLeft && + (outRec.IsHole == outRec.FirstLeft->IsHole || ! outRec.FirstLeft->Pts)) { + OutRec* orfl = outRec.FirstLeft; + while (orfl && ((orfl->IsHole == outRec.IsHole) || !orfl->Pts)) orfl = orfl->FirstLeft; - outRec->FirstLeft = orfl; + outRec.FirstLeft = orfl; } //nb: polytree takes ownership of all the PolyNodes polytree.AllNodes.emplace_back(PolyNode()); PolyNode* pn = &polytree.AllNodes.back(); - outRec->PolyNd = pn; + outRec.PolyNd = pn; pn->Parent = 0; pn->Index = 0; pn->Contour.reserve(cnt); - OutPt *op = outRec->Pts->Prev; + OutPt *op = outRec.Pts->Prev; for (int j = 0; j < cnt; j++) { pn->Contour.emplace_back(op->Pt); @@ -2836,18 +2812,18 @@ void Clipper::BuildResult2(PolyTree& polytree) //fixup PolyNode links etc ... polytree.Childs.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - if (!outRec->PolyNd) continue; - if (outRec->IsOpen) + if (!outRec.PolyNd) continue; + if (outRec.IsOpen) { - outRec->PolyNd->m_IsOpen = true; - polytree.AddChild(*outRec->PolyNd); + outRec.PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec.PolyNd); } - else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) - outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else if (outRec.FirstLeft && outRec.FirstLeft->PolyNd) + outRec.FirstLeft->PolyNd->AddChild(*outRec.PolyNd); else - polytree.AddChild(*outRec->PolyNd); + polytree.AddChild(*outRec.PolyNd); } } //------------------------------------------------------------------------------ @@ -3193,26 +3169,26 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) //---------------------------------------------------------------------- // This is potentially very expensive! O(n^3)! -void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) { //tests if NewOutRec contains the polygon before reassigning FirstLeft - for (OutRec *outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - if (!outRec->Pts || !outRec->FirstLeft) continue; - OutRec* firstLeft = outRec->FirstLeft; + if (!outRec.Pts || !outRec.FirstLeft) continue; + OutRec* firstLeft = outRec.FirstLeft; // Skip empty polygons. while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) - outRec->FirstLeft = NewOutRec; + if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec.Pts, NewOutRec->Pts)) + outRec.FirstLeft = NewOutRec; } } //---------------------------------------------------------------------- -void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const +void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) { //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon - for (OutRec *outRec : m_PolyOuts) - if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + for (OutRec &outRec : m_PolyOuts) + if (outRec.FirstLeft == OldOutRec) outRec.FirstLeft = NewOutRec; } //---------------------------------------------------------------------- @@ -3253,13 +3229,13 @@ void Clipper::JoinCommonEdges() if (m_UsingPolyTree) for (size_t j = 0; j < m_PolyOuts.size() - 1; j++) { - OutRec* oRec = m_PolyOuts[j]; - OutRec* firstLeft = oRec->FirstLeft; + OutRec &oRec = m_PolyOuts[j]; + OutRec* firstLeft = oRec.FirstLeft; while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (!oRec->Pts || firstLeft != outRec1 || - oRec->IsHole == outRec1->IsHole) continue; - if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2)) - oRec->FirstLeft = outRec2; + if (!oRec.Pts || firstLeft != outRec1 || + oRec.IsHole == outRec1->IsHole) continue; + if (Poly2ContainsPoly1(oRec.Pts, join.OutPt2)) + oRec.FirstLeft = outRec2; } if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) @@ -3771,13 +3747,13 @@ void Clipper::DoSimplePolygons() size_t i = 0; while (i < m_PolyOuts.size()) { - OutRec* outrec = m_PolyOuts[i++]; - OutPt* op = outrec->Pts; - if (!op || outrec->IsOpen) continue; + OutRec &outrec = m_PolyOuts[i++]; + OutPt* op = outrec.Pts; + if (!op || outrec.IsOpen) continue; do //for each Pt in Polygon until duplicate found do ... { OutPt* op2 = op->Next; - while (op2 != outrec->Pts) + while (op2 != outrec.Pts) { if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) { @@ -3789,37 +3765,37 @@ void Clipper::DoSimplePolygons() op2->Prev = op3; op3->Next = op2; - outrec->Pts = op; + outrec.Pts = op; OutRec* outrec2 = CreateOutRec(); outrec2->Pts = op2; UpdateOutPtIdxs(*outrec2); - if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + if (Poly2ContainsPoly1(outrec2->Pts, outrec.Pts)) { //OutRec2 is contained by OutRec1 ... - outrec2->IsHole = !outrec->IsHole; - outrec2->FirstLeft = outrec; + outrec2->IsHole = !outrec.IsHole; + outrec2->FirstLeft = &outrec; // For each m_PolyOuts, replace FirstLeft from outRec2 to outrec. - if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, &outrec); } else - if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + if (Poly2ContainsPoly1(outrec.Pts, outrec2->Pts)) { //OutRec1 is contained by OutRec2 ... - outrec2->IsHole = outrec->IsHole; - outrec->IsHole = !outrec2->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - outrec->FirstLeft = outrec2; + outrec2->IsHole = outrec.IsHole; + outrec.IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec.FirstLeft; + outrec.FirstLeft = outrec2; // For each m_PolyOuts, replace FirstLeft from outrec to outrec2. - if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + if (m_UsingPolyTree) FixupFirstLefts2(&outrec, outrec2); } else { //the 2 polygons are separate ... - outrec2->IsHole = outrec->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; + outrec2->IsHole = outrec.IsHole; + outrec2->FirstLeft = outrec.FirstLeft; // For each polygon of m_PolyOuts, replace FirstLeft from outrec to outrec2 if the polygon is inside outRec2. //FIXME This is potentially very expensive! O(n^3)! - if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + if (m_UsingPolyTree) FixupFirstLefts1(&outrec, outrec2); } op2 = op; //ie get ready for the Next iteration } @@ -3827,7 +3803,7 @@ void Clipper::DoSimplePolygons() } op = op->Next; } - while (op != outrec->Pts); + while (op != outrec.Pts); } } //------------------------------------------------------------------------------ @@ -3845,10 +3821,10 @@ void ReversePaths(Paths& p) } //------------------------------------------------------------------------------ -Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType) +Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType, bool strictly_simple /* = true */) { Clipper c; - c.StrictlySimple(true); + c.StrictlySimple(strictly_simple); c.AddPath(in_poly, ptSubject, true); Paths out; c.Execute(ctUnion, out, fillType, fillType); diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index d190d09b5d..c88545454e 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -52,6 +52,7 @@ //use_deprecated: Enables temporary support for the obsolete functions //#define use_deprecated +#include #include #include #include @@ -199,7 +200,8 @@ double Area(const Path &poly); inline bool Orientation(const Path &poly) { return Area(poly) >= 0; } int PointInPolygon(const IntPoint &pt, const Path &path); -Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftEvenOdd); +// Union with "strictly simple" fix enabled. +Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftNonZero, bool strictly_simple = true); void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); void CleanPolygon(Path& poly, double distance = 1.415); @@ -284,7 +286,25 @@ enum EdgeSide { esLeft = 1, esRight = 2}; using OutPts = std::vector>; - struct OutRec; + // Output polygon. + struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + //The 'FirstLeft' field points to another OutRec that contains or is the + //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is + //parsed left from the current edge (owning OutRec) until the owner OutRec + //is found. This field simplifies sorting the polygons into a tree structure + //which reflects the parent/child relationships of all polygons. + //This field should be renamed Parent, and will be later. + OutRec* FirstLeft; + // Used only by void Clipper::BuildResult2(PolyTree& polytree) + PolyNode* PolyNd; + // Linked list of output points, dynamically allocated. + OutPt* Pts; + OutPt* BottomPt; + }; + struct Join { Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {} @@ -432,12 +452,12 @@ protected: private: // Output polygons. - std::vector> m_PolyOuts; + std::deque> m_PolyOuts; // Output points, allocated by a continuous sets of m_OutPtsChunkSize. - std::vector> m_OutPts; + static constexpr const size_t m_OutPtsChunkSize = 32; + std::deque, Allocator>> m_OutPts; // List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk. OutPt *m_OutPtsFree; - size_t m_OutPtsChunkSize; size_t m_OutPtsChunkLast; std::vector> m_Joins; @@ -482,7 +502,7 @@ private: void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutRec* GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2) const; + void AppendPolygon(TEdge *e1, TEdge *e2); void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); @@ -498,7 +518,7 @@ private: void ProcessEdgesAtTopOfScanbeam(const cInt topY); void BuildResult(Paths& polys); void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *outrec) const; + void SetHoleState(TEdge *e, OutRec *outrec); bool FixupIntersectionOrder(); void FixupOutPolygon(OutRec &outrec); void FixupOutPolyline(OutRec &outrec); @@ -508,8 +528,8 @@ private: bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft); void JoinCommonEdges(); void DoSimplePolygons(); - void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; - void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec); #ifdef CLIPPERLIB_USE_XYZ void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); #endif @@ -567,10 +587,11 @@ class clipperException : public std::exception }; //------------------------------------------------------------------------------ +// Union with "strictly simple" fix enabled. template -inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftEvenOdd) { +inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftNonZero, bool strictly_simple = true) { Clipper c; - c.StrictlySimple(true); + c.StrictlySimple(strictly_simple); c.AddPaths(std::forward(in_polys), ptSubject, true); Paths out; c.Execute(ctUnion, out, fillType, fillType); diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index fce69d5e4c..6c5dafdac4 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -232,7 +232,7 @@ std::unique_ptr cre void fixSelfIntersections(const coord_t epsilon, Polygons &thiss) { if (epsilon < 1) { - ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); + ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd); return; } @@ -273,7 +273,7 @@ void fixSelfIntersections(const coord_t epsilon, Polygons &thiss) } } - ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); + ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd); } /*! diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 95a5337185..d10e14cc53 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -964,21 +964,18 @@ Polygons union_pt_chained_outside_in(const Polygons &subject) return retval; } -Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) +Polygons simplify_polygons(const Polygons &subject) { CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); ClipperLib::Paths output; - if (preserve_collinear) { - ClipperLib::Clipper c; - c.PreserveCollinear(true); - c.StrictlySimple(true); - c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); - c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); - } else { - output = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(subject), ClipperLib::pftNonZero); - } - + ClipperLib::Clipper c; +// c.PreserveCollinear(true); + //FIXME StrictlySimple is very expensive! Is it needed? + c.StrictlySimple(true); + c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); + c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + // convert into Slic3r polygons return to_polygons(std::move(output)); } @@ -987,12 +984,10 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear { CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); - if (! preserve_collinear) - return union_ex(simplify_polygons(subject, false)); - ClipperLib::PolyTree polytree; ClipperLib::Clipper c; - c.PreserveCollinear(true); +// c.PreserveCollinear(true); + //FIXME StrictlySimple is very expensive! Is it needed? c.StrictlySimple(true); c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index aaa06107da..ab967fd8fb 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -596,8 +596,8 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval) /* OTHER */ -Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); -Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false); +Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject); +Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject); Polygons top_level_islands(const Slic3r::Polygons &polygons); From fd437dcaf5f392dffb15eadcd359a4de92d0000b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 15:48:24 +0200 Subject: [PATCH 56/70] Little refactoring of douglas_peucker() --- src/libslic3r/ExPolygon.cpp | 4 ++-- src/libslic3r/Geometry.cpp | 16 ++++++------- src/libslic3r/MultiPoint.cpp | 2 +- src/libslic3r/MultiPoint.hpp | 2 +- src/libslic3r/Polygon.cpp | 45 +++++++++++++++++++++++++----------- src/libslic3r/Polygon.hpp | 3 ++- src/libslic3r/Polyline.cpp | 2 +- 7 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 42f026e0b2..19489bddbc 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -184,14 +184,14 @@ Polygons ExPolygon::simplify_p(double tolerance) const { Polygon p = this->contour; p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + p.points = MultiPoint::douglas_peucker(p.points, tolerance); p.points.pop_back(); pp.emplace_back(std::move(p)); } // holes for (Polygon p : this->holes) { p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + p.points = MultiPoint::douglas_peucker(p.points, tolerance); p.points.pop_back(); pp.emplace_back(std::move(p)); } diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 5542d73eee..f2860ea8e2 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -52,15 +52,15 @@ template bool contains(const ExPolygons &vector, const Point &point); void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) { - Polygons pp; - for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) { - Polygon p = *it; - p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); - p.points.pop_back(); - pp.push_back(p); + Polygons simplified_raw; + for (const Polygon &source_polygon : polygons) { + Points simplified = MultiPoint::douglas_peucker(to_polyline(source_polygon).points, tolerance); + if (simplified.size() > 3) { + simplified.pop_back(); + simplified_raw.push_back(Polygon{ std::move(simplified) }); + } } - *retval = Slic3r::simplify_polygons(pp); + *retval = Slic3r::simplify_polygons(simplified_raw); } double linint(double value, double oldmin, double oldmax, double newmin, double newmax) diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index bb4d62cc0b..ce54a54c00 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -103,7 +103,7 @@ bool MultiPoint::remove_duplicate_points() return false; } -Points MultiPoint::_douglas_peucker(const Points &pts, const double tolerance) +Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) { Points result_pts; double tolerance_sq = tolerance * tolerance; diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 4cf4b5e140..62b53255b4 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -81,7 +81,7 @@ public: } } - static Points _douglas_peucker(const Points &points, const double tolerance); + static Points douglas_peucker(const Points &points, const double tolerance); static Points visivalingam(const Points& pts, const double& tolerance); inline auto begin() { return points.begin(); } diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 299e22adcd..88ac1b03f8 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -96,7 +96,7 @@ bool Polygon::make_clockwise() void Polygon::douglas_peucker(double tolerance) { this->points.push_back(this->points.front()); - Points p = MultiPoint::_douglas_peucker(this->points, tolerance); + Points p = MultiPoint::douglas_peucker(this->points, tolerance); p.pop_back(); this->points = std::move(p); } @@ -110,7 +110,7 @@ Polygons Polygon::simplify(double tolerance) const // on the whole polygon Points points = this->points; points.push_back(points.front()); - Polygon p(MultiPoint::_douglas_peucker(points, tolerance)); + Polygon p(MultiPoint::douglas_peucker(points, tolerance)); p.points.pop_back(); Polygons pp; @@ -577,23 +577,40 @@ void remove_collinear(Polygons &polys) remove_collinear(poly); } -Polygons polygons_simplify(const Polygons &source_polygons, double tolerance) +static inline void simplify_polygon_impl(const Points &points, double tolerance, bool strictly_simple, Polygons &out) +{ + Points simplified = MultiPoint::douglas_peucker(points, tolerance); + // then remove the last (repeated) point. + simplified.pop_back(); + // Simplify the decimated contour by ClipperLib. + bool ccw = ClipperLib::Area(simplified) > 0.; + for (Points& path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero, strictly_simple)) { + if (!ccw) + // ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW. + std::reverse(path.begin(), path.end()); + out.emplace_back(std::move(path)); + } +} + +Polygons polygons_simplify(Polygons &&source_polygons, double tolerance, bool strictly_simple /* = true */) +{ + Polygons out; + out.reserve(source_polygons.size()); + for (Polygon &source_polygon : source_polygons) { + // Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline), + source_polygon.points.emplace_back(source_polygon.points.front()); + simplify_polygon_impl(source_polygon.points, tolerance, strictly_simple, out); + } + return out; +} + +Polygons polygons_simplify(const Polygons &source_polygons, double tolerance, bool strictly_simple /* = true */) { Polygons out; out.reserve(source_polygons.size()); for (const Polygon &source_polygon : source_polygons) { // Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline), - Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance); - // then remove the last (repeated) point. - simplified.pop_back(); - // Simplify the decimated contour by ClipperLib. - bool ccw = ClipperLib::Area(simplified) > 0.; - for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero)) { - if (! ccw) - // ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW. - std::reverse(path.begin(), path.end()); - out.emplace_back(std::move(path)); - } + simplify_polygon_impl(to_polyline(source_polygon).points, tolerance, strictly_simple, out); } return out; } diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index bf4a087b0d..e0c3958fd9 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -149,7 +149,8 @@ inline void polygons_append(Polygons &dst, Polygons &&src) } } -Polygons polygons_simplify(const Polygons &polys, double tolerance); +Polygons polygons_simplify(Polygons &&polys, double tolerance, bool strictly_simple = true); +Polygons polygons_simplify(const Polygons &polys, double tolerance, bool strictly_simple = true); inline void polygons_rotate(Polygons &polys, double angle) { diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 5743e38bd5..5247365752 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -110,7 +110,7 @@ Points Polyline::equally_spaced_points(double distance) const void Polyline::simplify(double tolerance) { - this->points = MultiPoint::_douglas_peucker(this->points, tolerance); + this->points = MultiPoint::douglas_peucker(this->points, tolerance); } #if 0 From c3178321b46008512a9f38259e49827113c4795e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 2 May 2023 15:53:08 +0200 Subject: [PATCH 57/70] Optimization of triangle mesh slicing: scalable_allocator and hashing of shared mutexes. --- src/libslic3r/TriangleMeshSlicer.cpp | 42 ++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 460cd901e9..0261b6121b 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -15,6 +15,9 @@ #include #include +#include + +#include #ifndef NDEBUG // #define EXPENSIVE_DEBUG_CHECKS @@ -139,7 +142,7 @@ public: #endif }; -using IntersectionLines = std::vector; +using IntersectionLines = std::vector>; enum class FacetSliceType { NoSlice = 0, @@ -351,6 +354,21 @@ inline FacetSliceType slice_facet( return FacetSliceType::NoSlice; } +class LinesMutexes { +public: + std::mutex& operator()(size_t slice_id) { + ankerl::unordered_dense::hash hash; + return m_mutexes[hash(slice_id) % m_mutexes.size()].mutex; + } + +private: + struct CacheLineAlignedMutex + { + alignas(std::hardware_destructive_interference_size) std::mutex mutex; + }; + std::array m_mutexes; +}; + template void slice_facet_at_zs( // Scaled or unscaled vertices. transform_vertex_fn may scale zs. @@ -361,7 +379,7 @@ void slice_facet_at_zs( // Scaled or unscaled zs. If vertices have their zs scaled or transform_vertex_fn scales them, then zs have to be scaled as well. const std::vector &zs, std::vector &lines, - std::array &lines_mutex) + LinesMutexes &lines_mutex) { stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) }; @@ -380,7 +398,7 @@ void slice_facet_at_zs( if (min_z != max_z && slice_facet(*it, vertices, indices, edge_ids, idx_vertex_lowest, false, il) == FacetSliceType::Slicing) { assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal); size_t slice_id = it - zs.begin(); - boost::lock_guard l(lines_mutex[slice_id % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(slice_id)); lines[slice_id].emplace_back(il); } } @@ -395,8 +413,8 @@ static inline std::vector slice_make_lines( const std::vector &zs, const ThrowOnCancel throw_on_cancel_fn) { - std::vector lines(zs.size(), IntersectionLines()); - std::array lines_mutex; + std::vector lines(zs.size(), IntersectionLines{}); + LinesMutexes lines_mutex; tbb::parallel_for( tbb::blocked_range(0, int(indices.size())), [&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range &range) { @@ -475,7 +493,7 @@ void slice_facet_with_slabs( const int num_edges, const std::vector &zs, SlabLines &lines, - std::array &lines_mutex) + LinesMutexes &lines_mutex) { const stl_triangle_vertex_indices &indices = mesh_triangles[facet_idx]; stl_vertex vertices[3] { mesh_vertices[indices(0)], mesh_vertices[indices(1)], mesh_vertices[indices(2)] }; @@ -494,7 +512,7 @@ void slice_facet_with_slabs( auto emit_slab_edge = [&lines, &lines_mutex](IntersectionLine il, size_t slab_id, bool reverse) { if (reverse) il.reverse(); - boost::lock_guard l(lines_mutex[(slab_id + lines_mutex.size() / 2) % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(slab_id)); lines.between_slices[slab_id].emplace_back(il); }; @@ -530,7 +548,7 @@ void slice_facet_with_slabs( }; // Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining. // if (! ProjectionFromTop) il.reverse(); - boost::lock_guard l(lines_mutex[line_id % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(line_id)); lines.at_slice[line_id].emplace_back(il); } } else { @@ -649,7 +667,7 @@ void slice_facet_with_slabs( if (! ProjectionFromTop) il.reverse(); size_t line_id = it - zs.begin(); - boost::lock_guard l(lines_mutex[line_id % lines_mutex.size()]); + boost::lock_guard l(lines_mutex(line_id)); lines.at_slice[line_id].emplace_back(il); } } @@ -804,8 +822,8 @@ inline std::pair slice_slabs_make_lines( std::pair out; SlabLines &lines_top = out.first; SlabLines &lines_bottom = out.second; - std::array lines_mutex_top; - std::array lines_mutex_bottom; + LinesMutexes lines_mutex_top; + LinesMutexes lines_mutex_bottom; if (top) { lines_top.at_slice.assign(zs.size(), IntersectionLines()); @@ -1540,7 +1558,7 @@ static std::vector make_slab_loops( } // Used to cut the mesh into two halves. -static ExPolygons make_expolygons_simple(std::vector &lines) +static ExPolygons make_expolygons_simple(IntersectionLines &lines) { ExPolygons slices; Polygons holes; From 00ea0847b81d3229135444ba9f76fd2f5cbf5e7a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 4 May 2023 17:25:26 +0200 Subject: [PATCH 58/70] Fix of merging bridging regions: Fixed building a DAG of overlapping regions in expand_bridges_detect_orientations() --- src/libslic3r/LayerRegion.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index d722f1e9ce..ab52dd962b 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -230,10 +230,13 @@ Surfaces expand_bridges_detect_orientations( bboxes[it - it_begin].overlap(bboxes[it2 - it_begin]) && // One may ignore holes, they are irrelevant for intersection test. ! intersection(it->expolygon.contour, it2->expolygon.contour).empty()) { - // The two bridge regions intersect. Give them the same group id. + // The two bridge regions intersect. Give them the same (lower) group id. uint32_t id = group_id(it->src_id); uint32_t id2 = group_id(it2->src_id); - bridges[it->src_id].group_id = bridges[it2->src_id].group_id = std::min(id, id2); + if (id < id2) + bridges[id2].group_id = id; + else + bridges[id].group_id = id2; } } } From 1bbe0c5be3bcea11b0f26a3fc2b11037e15a5fe0 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 4 May 2023 17:39:31 +0200 Subject: [PATCH 59/70] fix missing include for linux builds --- src/libslic3r/TreeModelVolumes.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index 10840d1b5a..96f61a07da 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -17,6 +17,7 @@ #include "Print.hpp" #include "PrintConfig.hpp" #include "Utils.hpp" +#include "format.hpp" #include From cc938e754949bed278daac5e07c2639a9f54e86e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 5 May 2023 10:21:15 +0200 Subject: [PATCH 60/70] WIP SupportGeneratorLayerStorage refactoring. --- src/libslic3r/CMakeLists.txt | 6 +- src/libslic3r/Support/SupportLayer.hpp | 130 +++++++++++++++ src/libslic3r/Support/SupportParameters.hpp | 66 ++++++++ src/libslic3r/SupportMaterial.hpp | 165 +------------------- src/libslic3r/TreeSupport.hpp | 4 +- src/libslic3r/pchheader.hpp | 1 + 6 files changed, 205 insertions(+), 167 deletions(-) create mode 100644 src/libslic3r/Support/SupportLayer.hpp create mode 100644 src/libslic3r/Support/SupportParameters.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 53074d3bd8..32d44cb5cc 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -276,10 +276,12 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp - SupportSpotsGenerator.cpp - SupportSpotsGenerator.hpp + Support/SupportLayer.hpp + Support/SupportParameters.hpp SupportMaterial.cpp SupportMaterial.hpp + SupportSpotsGenerator.cpp + SupportSpotsGenerator.hpp Surface.cpp Surface.hpp SurfaceCollection.cpp diff --git a/src/libslic3r/Support/SupportLayer.hpp b/src/libslic3r/Support/SupportLayer.hpp new file mode 100644 index 0000000000..913e28136c --- /dev/null +++ b/src/libslic3r/Support/SupportLayer.hpp @@ -0,0 +1,130 @@ +#ifndef slic3r_SupportLayer_hpp_ +#define slic3r_SupportLayer_hpp_ + +#include +#include + +namespace Slic3r { + +// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information +// about the support layer type than the final support layers stored in a PrintObject. +enum class SupporLayerType { + Unknown = 0, + // Ratft base layer, to be printed with the support material. + RaftBase, + // Raft interface layer, to be printed with the support interface material. + RaftInterface, + // Bottom contact layer placed over a top surface of an object. To be printed with a support interface material. + BottomContact, + // Dense interface layer, to be printed with the support interface material. + // This layer is separated from an object by an BottomContact layer. + BottomInterface, + // Sparse base support layer, to be printed with a support material. + Base, + // Dense interface layer, to be printed with the support interface material. + // This layer is separated from an object with TopContact layer. + TopInterface, + // Top contact layer directly supporting an overhang. To be printed with a support interface material. + TopContact, + // Some undecided type yet. It will turn into Base first, then it may turn into BottomInterface or TopInterface. + Intermediate, +}; + +// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed +// information about the support layer than the layers stored in the PrintObject, mainly +// the SupportGeneratorLayer is aware of the bridging flow and the interface gaps between the object and the support. +class SupportGeneratorLayer +{ +public: + void reset() { + *this = SupportGeneratorLayer(); + } + + bool operator==(const SupportGeneratorLayer &layer2) const { + return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging; + } + + // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. + bool operator<(const SupportGeneratorLayer &layer2) const { + if (print_z < layer2.print_z) { + return true; + } else if (print_z == layer2.print_z) { + if (height > layer2.height) + return true; + else if (height == layer2.height) { + // Bridging layers first. + return bridging && ! layer2.bridging; + } else + return false; + } else + return false; + } + + void merge(SupportGeneratorLayer &&rhs) { + // The union_() does not support move semantic yet, but maybe one day it will. + this->polygons = union_(this->polygons, std::move(rhs.polygons)); + auto merge = [](std::unique_ptr &dst, std::unique_ptr &src) { + if (! dst || dst->empty()) + dst = std::move(src); + else if (src && ! src->empty()) + *dst = union_(*dst, std::move(*src)); + }; + merge(this->contact_polygons, rhs.contact_polygons); + merge(this->overhang_polygons, rhs.overhang_polygons); + merge(this->enforcer_polygons, rhs.enforcer_polygons); + rhs.reset(); + } + + // For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation. + // For the non-bridging flow, bottom_print_z will be equal to bottom_z. + coordf_t bottom_print_z() const { return print_z - height; } + + // To sort the extremes of top / bottom interface layers. + coordf_t extreme_z() const { return (this->layer_type == SupporLayerType::TopContact) ? this->bottom_z : this->print_z; } + + SupporLayerType layer_type { SupporLayerType::Unknown }; + // Z used for printing, in unscaled coordinates. + coordf_t print_z { 0 }; + // Bottom Z of this layer. For soluble layers, bottom_z + height = print_z, + // otherwise bottom_z + gap + height = print_z. + coordf_t bottom_z { 0 }; + // Layer height in unscaled coordinates. + coordf_t height { 0 }; + // Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers. + // If this is not a contact layer, it will be set to size_t(-1). + size_t idx_object_layer_above { size_t(-1) }; + // Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers. + // If this is not a contact layer, it will be set to size_t(-1). + size_t idx_object_layer_below { size_t(-1) }; + // Use a bridging flow when printing this support layer. + bool bridging { false }; + + // Polygons to be filled by the support pattern. + Polygons polygons; + // Currently for the contact layers only. + std::unique_ptr contact_polygons; + std::unique_ptr overhang_polygons; + // Enforcers need to be propagated independently in case the "support on build plate only" option is enabled. + std::unique_ptr enforcer_polygons; +}; + +// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained +// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future, +// which would allocate layers by multiple chunks. +#if 0 +class SupportGeneratorLayerStorage { +public: +private: + template + using Allocator = tbb::scalable_allocator; + Slic3r::deque> m_data; + tbb::spin_mutex m_mutex; +}; +#else +#endif +using SupportGeneratorLayerStorage = std::deque; +using SupportGeneratorLayersPtr = std::vector; + +} // namespace Slic3r + +#endif /* slic3r_SupportLayer_hpp_ */ diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp new file mode 100644 index 0000000000..fd4f1f8b78 --- /dev/null +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -0,0 +1,66 @@ +#ifndef slic3r_SupportParameters_hpp_ +#define slic3r_SupportParameters_hpp_ + +#include "../libslic3r.h" +#include "../Flow.hpp" + +namespace Slic3r { + +class PrintObject; +enum InfillPattern : int; + +struct SupportParameters { + SupportParameters(const PrintObject &object); + + // Flow at the 1st print layer. + Flow first_layer_flow; + // Flow at the support base (neither top, nor bottom interface). + // Also flow at the raft base with the exception of raft interface and contact layers. + Flow support_material_flow; + // Flow at the top interface and contact layers. + Flow support_material_interface_flow; + // Flow at the bottom interfaces and contacts. + Flow support_material_bottom_interface_flow; + // Flow at raft inteface & contact layers. + Flow raft_interface_flow; + // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? + bool can_merge_support_regions; + + coordf_t support_layer_height_min; +// coordf_t support_layer_height_max; + + coordf_t gap_xy; + + float base_angle; + float interface_angle; + + // Density of the top / bottom interface and contact layers. + coordf_t interface_density; + // Density of the raft interface and contact layers. + coordf_t raft_interface_density; + // Density of the base support layers. + coordf_t support_density; + + // Pattern of the sparse infill including sparse raft layers. + InfillPattern base_fill_pattern; + // Pattern of the top / bottom interface and contact layers. + InfillPattern interface_fill_pattern; + // Pattern of the raft interface and contact layers. + InfillPattern raft_interface_fill_pattern; + // Pattern of the contact layers. + InfillPattern contact_fill_pattern; + // Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness? + bool with_sheath; + + float raft_angle_1st_layer; + float raft_angle_base; + float raft_angle_interface; + + // Produce a raft interface angle for a given SupportLayer::interface_id() + float raft_interface_angle(size_t interface_id) const + { return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); } +}; + +} // namespace Slic3r + +#endif /* slic3r_SupportParameters_hpp_ */ diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index 2bd3211445..24dc8507e7 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -5,171 +5,12 @@ #include "PrintConfig.hpp" #include "Slicing.hpp" +#include "Support/SupportLayer.hpp" +#include "Support/SupportParameters.hpp" + namespace Slic3r { class PrintObject; -class PrintConfig; -class PrintObjectConfig; - -// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information -// about the support layer type than the final support layers stored in a PrintObject. -enum class SupporLayerType { - Unknown = 0, - // Ratft base layer, to be printed with the support material. - RaftBase, - // Raft interface layer, to be printed with the support interface material. - RaftInterface, - // Bottom contact layer placed over a top surface of an object. To be printed with a support interface material. - BottomContact, - // Dense interface layer, to be printed with the support interface material. - // This layer is separated from an object by an BottomContact layer. - BottomInterface, - // Sparse base support layer, to be printed with a support material. - Base, - // Dense interface layer, to be printed with the support interface material. - // This layer is separated from an object with TopContact layer. - TopInterface, - // Top contact layer directly supporting an overhang. To be printed with a support interface material. - TopContact, - // Some undecided type yet. It will turn into Base first, then it may turn into BottomInterface or TopInterface. - Intermediate, -}; - -// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed -// information about the support layer than the layers stored in the PrintObject, mainly -// the SupportGeneratorLayer is aware of the bridging flow and the interface gaps between the object and the support. -class SupportGeneratorLayer -{ -public: - void reset() { - *this = SupportGeneratorLayer(); - } - - bool operator==(const SupportGeneratorLayer &layer2) const { - return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging; - } - - // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. - bool operator<(const SupportGeneratorLayer &layer2) const { - if (print_z < layer2.print_z) { - return true; - } else if (print_z == layer2.print_z) { - if (height > layer2.height) - return true; - else if (height == layer2.height) { - // Bridging layers first. - return bridging && ! layer2.bridging; - } else - return false; - } else - return false; - } - - void merge(SupportGeneratorLayer &&rhs) { - // The union_() does not support move semantic yet, but maybe one day it will. - this->polygons = union_(this->polygons, std::move(rhs.polygons)); - auto merge = [](std::unique_ptr &dst, std::unique_ptr &src) { - if (! dst || dst->empty()) - dst = std::move(src); - else if (src && ! src->empty()) - *dst = union_(*dst, std::move(*src)); - }; - merge(this->contact_polygons, rhs.contact_polygons); - merge(this->overhang_polygons, rhs.overhang_polygons); - merge(this->enforcer_polygons, rhs.enforcer_polygons); - rhs.reset(); - } - - // For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation. - // For the non-bridging flow, bottom_print_z will be equal to bottom_z. - coordf_t bottom_print_z() const { return print_z - height; } - - // To sort the extremes of top / bottom interface layers. - coordf_t extreme_z() const { return (this->layer_type == SupporLayerType::TopContact) ? this->bottom_z : this->print_z; } - - SupporLayerType layer_type { SupporLayerType::Unknown }; - // Z used for printing, in unscaled coordinates. - coordf_t print_z { 0 }; - // Bottom Z of this layer. For soluble layers, bottom_z + height = print_z, - // otherwise bottom_z + gap + height = print_z. - coordf_t bottom_z { 0 }; - // Layer height in unscaled coordinates. - coordf_t height { 0 }; - // Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers. - // If this is not a contact layer, it will be set to size_t(-1). - size_t idx_object_layer_above { size_t(-1) }; - // Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers. - // If this is not a contact layer, it will be set to size_t(-1). - size_t idx_object_layer_below { size_t(-1) }; - // Use a bridging flow when printing this support layer. - bool bridging { false }; - - // Polygons to be filled by the support pattern. - Polygons polygons; - // Currently for the contact layers only. - std::unique_ptr contact_polygons; - std::unique_ptr overhang_polygons; - // Enforcers need to be propagated independently in case the "support on build plate only" option is enabled. - std::unique_ptr enforcer_polygons; -}; - -// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained -// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future, -// which would allocate layers by multiple chunks. -using SupportGeneratorLayerStorage = std::deque; -using SupportGeneratorLayersPtr = std::vector; - -struct SupportParameters { - SupportParameters(const PrintObject &object); - - // Flow at the 1st print layer. - Flow first_layer_flow; - // Flow at the support base (neither top, nor bottom interface). - // Also flow at the raft base with the exception of raft interface and contact layers. - Flow support_material_flow; - // Flow at the top interface and contact layers. - Flow support_material_interface_flow; - // Flow at the bottom interfaces and contacts. - Flow support_material_bottom_interface_flow; - // Flow at raft inteface & contact layers. - Flow raft_interface_flow; - // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? - bool can_merge_support_regions; - - coordf_t support_layer_height_min; -// coordf_t support_layer_height_max; - - coordf_t gap_xy; - - float base_angle; - float interface_angle; - - // Density of the top / bottom interface and contact layers. - coordf_t interface_density; - // Density of the raft interface and contact layers. - coordf_t raft_interface_density; - // Density of the base support layers. - coordf_t support_density; - - // Pattern of the sparse infill including sparse raft layers. - InfillPattern base_fill_pattern; - // Pattern of the top / bottom interface and contact layers. - InfillPattern interface_fill_pattern; - // Pattern of the raft interface and contact layers. - InfillPattern raft_interface_fill_pattern; - // Pattern of the contact layers. - InfillPattern contact_fill_pattern; - // Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness? - bool with_sheath; - - float raft_angle_1st_layer; - float raft_angle_base; - float raft_angle_interface; - - // Produce a raft interface angle for a given SupportLayer::interface_id() - float raft_interface_angle(size_t interface_id) const - { return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); } -}; // Remove bridges from support contact areas. // To be called if PrintObjectConfig::dont_support_bridges. diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index 2c87a132d6..2ed5d50ec3 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -11,6 +11,7 @@ #include "TreeModelVolumes.hpp" #include "Point.hpp" +#include "Support/SupportLayer.hpp" #include @@ -39,10 +40,7 @@ namespace Slic3r // Forward declarations class Print; class PrintObject; -class SupportGeneratorLayer; struct SlicingParameters; -using SupportGeneratorLayerStorage = std::deque; -using SupportGeneratorLayersPtr = std::vector; namespace FFFTreeSupport { diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp index 9017a5deac..e71b1461ce 100644 --- a/src/libslic3r/pchheader.hpp +++ b/src/libslic3r/pchheader.hpp @@ -107,6 +107,7 @@ #include #include +#include #include #include From a236351fd3043e6a8a007830aaabc423387b1c6c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 5 May 2023 12:59:01 +0200 Subject: [PATCH 61/70] Supports refactoring: Split FFF supports into multiple files, enclosed into namespaces. --- src/libslic3r/CMakeLists.txt | 5 + src/libslic3r/Support/SupportCommon.cpp | 1807 +++++++++++++++++ src/libslic3r/Support/SupportCommon.hpp | 138 ++ src/libslic3r/Support/SupportDebug.cpp | 108 + src/libslic3r/Support/SupportDebug.hpp | 18 + src/libslic3r/Support/SupportLayer.hpp | 26 +- src/libslic3r/Support/SupportParameters.cpp | 116 ++ src/libslic3r/Support/SupportParameters.hpp | 4 + src/libslic3r/SupportMaterial.cpp | 2013 +------------------ src/libslic3r/SupportMaterial.hpp | 52 +- src/libslic3r/TreeSupport.cpp | 44 +- 11 files changed, 2259 insertions(+), 2072 deletions(-) create mode 100644 src/libslic3r/Support/SupportCommon.cpp create mode 100644 src/libslic3r/Support/SupportCommon.hpp create mode 100644 src/libslic3r/Support/SupportDebug.cpp create mode 100644 src/libslic3r/Support/SupportDebug.hpp create mode 100644 src/libslic3r/Support/SupportParameters.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 32d44cb5cc..11f7adf766 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -276,7 +276,12 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp + Support/SupportCommon.cpp + Support/SupportCommon.hpp + Support/SupportDebug.cpp + Support/SupportDebug.hpp Support/SupportLayer.hpp + Support/SupportParameters.cpp Support/SupportParameters.hpp SupportMaterial.cpp SupportMaterial.hpp diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp new file mode 100644 index 0000000000..8825d8dd23 --- /dev/null +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -0,0 +1,1807 @@ +#include "../ClipperUtils.hpp" +#include "../ExtrusionEntityCollection.hpp" +#include "../Layer.hpp" +#include "../Print.hpp" +#include "../Fill/FillBase.hpp" +#include "../Geometry.hpp" +#include "../Point.hpp" + +#include +#include + +#include + +#include "SupportCommon.hpp" +#include "SupportLayer.hpp" +#include "SupportParameters.hpp" + +// #define SLIC3R_DEBUG + +// Make assert active if SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG + #define DEBUG + #define _DEBUG + #undef NDEBUG + #include "utils.hpp" + #include "SVG.hpp" +#endif + +#include + +namespace Slic3r::FFFSupport { + +// how much we extend support around the actual contact area +//FIXME this should be dependent on the nozzle diameter! +#define SUPPORT_MATERIAL_MARGIN 1.5 + +//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3. +//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 +#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. + +void remove_bridges_from_contacts( + const PrintConfig &print_config, + const Layer &lower_layer, + const LayerRegion &layerm, + float fw, + Polygons &contact_polygons) +{ + // compute the area of bridging perimeters + Polygons bridges; + { + // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported. + Polygons lower_grown_slices = expand(lower_layer.lslices, + //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width. + 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm.region().config().perimeter_extruder-1))), + SUPPORT_SURFACES_OFFSET_PARAMETERS); + // Collect perimeters of this layer. + //FIXME split_at_first_point() could split a bridge mid-way + #if 0 + Polylines overhang_perimeters = layerm.perimeters.as_polylines(); + // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() + for (Polyline &polyline : overhang_perimeters) + polyline.points[0].x += 1; + // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters. + overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); + #else + Polylines overhang_perimeters = diff_pl(layerm.perimeters().as_polylines(), lower_grown_slices); + #endif + + // only consider straight overhangs + // only consider overhangs having endpoints inside layer's slices + // convert bridging polylines into polygons by inflating them with their thickness + // since we're dealing with bridges, we can't assume width is larger than spacing, + // so we take the largest value and also apply safety offset to be ensure no gaps + // are left in between + Flow perimeter_bridge_flow = layerm.bridging_flow(frPerimeter); + //FIXME one may want to use a maximum of bridging flow width and normal flow width, as the perimeters are calculated using the normal flow + // and then turned to bridging flow, thus their centerlines are derived from non-bridging flow and expanding them by a bridging flow + // may not expand them to the edge of their respective islands. + const float w = float(0.5 * std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())) + scaled(0.001); + for (Polyline &polyline : overhang_perimeters) + if (polyline.is_straight()) { + // This is a bridge + polyline.extend_start(fw); + polyline.extend_end(fw); + // Is the straight perimeter segment supported at both sides? + Point pts[2] = { polyline.first_point(), polyline.last_point() }; + bool supported[2] = { false, false }; + for (size_t i = 0; i < lower_layer.lslices.size() && ! (supported[0] && supported[1]); ++ i) + for (int j = 0; j < 2; ++ j) + if (! supported[j] && lower_layer.lslices_ex[i].bbox.contains(pts[j]) && lower_layer.lslices[i].contains(pts[j])) + supported[j] = true; + if (supported[0] && supported[1]) + // Offset a polyline into a thick line. + polygons_append(bridges, offset(polyline, w)); + } + bridges = union_(bridges); + } + // remove the entire bridges and only support the unsupported edges + //FIXME the brided regions are already collected as layerm.bridged. Use it? + for (const Surface &surface : layerm.fill_surfaces()) + if (surface.surface_type == stBottomBridge && surface.bridge_angle >= 0.0) + polygons_append(bridges, surface.expolygon); + //FIXME add the gap filled areas. Extrude the gaps with a bridge flow? + // Remove the unsupported ends of the bridges from the bridged areas. + //FIXME add supports at regular intervals to support long bridges! + bridges = diff(bridges, + // Offset unsupported edges into polygons. + offset(layerm.unsupported_bridge_edges(), scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); + // Remove bridged areas from the supported areas. + contact_polygons = diff(contact_polygons, bridges, ApplySafetyOffset::Yes); + + #ifdef SLIC3R_DEBUG + static int iRun = 0; + SVG::export_expolygons(debug_out_path("support-top-contacts-remove-bridges-run%d.svg", iRun ++), + { { { union_ex(offset(layerm.unsupported_bridge_edges(), scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)) }, { "unsupported_bridge_edges", "orange", 0.5f } }, + { { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } }, + { { union_ex(bridges) }, { "bridges", "red", "black", "", scaled(0.1f), 0.5f } } }); + #endif /* SLIC3R_DEBUG */ +} + +SupportGeneratorLayersPtr generate_raft_base( + const PrintObject &object, + const SupportParameters &support_params, + const SlicingParameters &slicing_params, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers, + const SupportGeneratorLayersPtr &base_layers, + SupportGeneratorLayerStorage &layer_storage) +{ + // If there is brim to be generated, calculate the trimming regions. + Polygons brim; + if (object.has_brim()) { + // The object does not have a raft. + // Calculate the area covered by the brim. + const BrimType brim_type = object.config().brim_type; + const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner; + const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner; + const auto brim_separation = scaled(object.config().brim_separation.value + object.config().brim_width.value); + for (const ExPolygon &ex : object.layers().front()->lslices) { + if (brim_outer && brim_inner) + polygons_append(brim, offset(ex, brim_separation)); + else { + if (brim_outer) + polygons_append(brim, offset(ex.contour, brim_separation, ClipperLib::jtRound, float(scale_(0.1)))); + else + brim.emplace_back(ex.contour); + if (brim_inner) { + Polygons holes = ex.holes; + polygons_reverse(holes); + holes = shrink(holes, brim_separation, ClipperLib::jtRound, float(scale_(0.1))); + polygons_reverse(holes); + polygons_append(brim, std::move(holes)); + } else + polygons_append(brim, ex.holes); + } + } + brim = union_(brim); + } + + // How much to inflate the support columns to be stable. This also applies to the 1st layer, if no raft layers are to be printed. + const float inflate_factor_fine = float(scale_((slicing_params.raft_layers() > 1) ? 0.5 : EPSILON)); + const float inflate_factor_1st_layer = std::max(0.f, float(scale_(object.config().raft_first_layer_expansion)) - inflate_factor_fine); + SupportGeneratorLayer *contacts = top_contacts .empty() ? nullptr : top_contacts .front(); + SupportGeneratorLayer *interfaces = interface_layers .empty() ? nullptr : interface_layers .front(); + SupportGeneratorLayer *base_interfaces = base_interface_layers.empty() ? nullptr : base_interface_layers.front(); + SupportGeneratorLayer *columns_base = base_layers .empty() ? nullptr : base_layers .front(); + if (contacts != nullptr && contacts->print_z > std::max(slicing_params.first_print_layer_height, slicing_params.raft_contact_top_z) + EPSILON) + // This is not the raft contact layer. + contacts = nullptr; + if (interfaces != nullptr && interfaces->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) + // This is not the raft column base layer. + interfaces = nullptr; + if (base_interfaces != nullptr && base_interfaces->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) + // This is not the raft column base layer. + base_interfaces = nullptr; + if (columns_base != nullptr && columns_base->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) + // This is not the raft interface layer. + columns_base = nullptr; + + Polygons interface_polygons; + if (contacts != nullptr && ! contacts->polygons.empty()) + polygons_append(interface_polygons, expand(contacts->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + if (interfaces != nullptr && ! interfaces->polygons.empty()) + polygons_append(interface_polygons, expand(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + if (base_interfaces != nullptr && ! base_interfaces->polygons.empty()) + polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + + // Output vector. + SupportGeneratorLayersPtr raft_layers; + + if (slicing_params.raft_layers() > 1) { + Polygons base; + Polygons columns; + Polygons first_layer; + if (columns_base != nullptr) { + if (columns_base->bottom_print_z() > slicing_params.raft_interface_top_z - EPSILON) { + // Classic supports with colums above the raft interface. + base = columns_base->polygons; + columns = base; + if (! interface_polygons.empty()) + // Trim the 1st layer columns with the inflated interface polygons. + columns = diff(columns, interface_polygons); + } else { + // Organic supports with raft on print bed. + assert(is_approx(columns_base->print_z, slicing_params.first_print_layer_height)); + first_layer = columns_base->polygons; + } + } + if (! interface_polygons.empty()) { + // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. + base = union_(base, interface_polygons); + } + // Do not add the raft contact layer, only add the raft layers below the contact layer. + // Insert the 1st layer. + { + SupportGeneratorLayer &new_layer = layer_storage.allocate_unguarded(slicing_params.base_raft_layers > 0 ? SupporLayerType::RaftBase : SupporLayerType::RaftInterface); + raft_layers.push_back(&new_layer); + new_layer.print_z = slicing_params.first_print_layer_height; + new_layer.height = slicing_params.first_print_layer_height; + new_layer.bottom_z = 0.; + first_layer = union_(std::move(first_layer), base); + new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(first_layer, inflate_factor_1st_layer) : first_layer; + } + // Insert the base layers. + for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { + coordf_t print_z = raft_layers.back()->print_z; + SupportGeneratorLayer &new_layer = layer_storage.allocate_unguarded(SupporLayerType::RaftBase); + raft_layers.push_back(&new_layer); + new_layer.print_z = print_z + slicing_params.base_raft_layer_height; + new_layer.height = slicing_params.base_raft_layer_height; + new_layer.bottom_z = print_z; + new_layer.polygons = base; + } + // Insert the interface layers. + for (size_t i = 1; i < slicing_params.interface_raft_layers; ++ i) { + coordf_t print_z = raft_layers.back()->print_z; + SupportGeneratorLayer &new_layer = layer_storage.allocate_unguarded(SupporLayerType::RaftInterface); + raft_layers.push_back(&new_layer); + new_layer.print_z = print_z + slicing_params.interface_raft_layer_height; + new_layer.height = slicing_params.interface_raft_layer_height; + new_layer.bottom_z = print_z; + new_layer.polygons = interface_polygons; + //FIXME misusing contact_polygons for support columns. + new_layer.contact_polygons = std::make_unique(columns); + } + } else { + if (columns_base != nullptr) { + // Expand the bases of the support columns in the 1st layer. + Polygons &raft = columns_base->polygons; + Polygons trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); + if (inflate_factor_1st_layer > SCALED_EPSILON) { + // Inflate in multiple steps to avoid leaking of the support 1st layer through object walls. + auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / support_params.first_layer_flow.scaled_width()))); + float step = inflate_factor_1st_layer / nsteps; + for (int i = 0; i < nsteps; ++ i) + raft = diff(expand(raft, step), trimming); + } else + raft = diff(raft, trimming); + if (! interface_polygons.empty()) + columns_base->polygons = diff(columns_base->polygons, interface_polygons); + } + if (! brim.empty()) { + if (columns_base) + columns_base->polygons = diff(columns_base->polygons, brim); + if (contacts) + contacts->polygons = diff(contacts->polygons, brim); + if (interfaces) + interfaces->polygons = diff(interfaces->polygons, brim); + if (base_interfaces) + base_interfaces->polygons = diff(base_interfaces->polygons, brim); + } + } + + return raft_layers; +} + +static inline void fill_expolygon_generate_paths( + ExtrusionEntitiesPtr &dst, + ExPolygon &&expolygon, + Fill *filler, + const FillParams &fill_params, + float density, + ExtrusionRole role, + const Flow &flow) +{ + Surface surface(stInternal, std::move(expolygon)); + Polylines polylines; + try { + assert(!fill_params.use_arachne); + polylines = filler->fill_surface(&surface, fill_params); + } catch (InfillFailedException &) { + } + extrusion_entities_append_paths( + dst, + std::move(polylines), + role, + flow.mm3_per_mm(), flow.width(), flow.height()); +} + +static inline void fill_expolygons_generate_paths( + ExtrusionEntitiesPtr &dst, + ExPolygons &&expolygons, + Fill *filler, + const FillParams &fill_params, + float density, + ExtrusionRole role, + const Flow &flow) +{ + for (ExPolygon &expoly : expolygons) + fill_expolygon_generate_paths(dst, std::move(expoly), filler, fill_params, density, role, flow); +} + +static inline void fill_expolygons_generate_paths( + ExtrusionEntitiesPtr &dst, + ExPolygons &&expolygons, + Fill *filler, + float density, + ExtrusionRole role, + const Flow &flow) +{ + FillParams fill_params; + fill_params.density = density; + fill_params.dont_adjust = true; + fill_expolygons_generate_paths(dst, std::move(expolygons), filler, fill_params, density, role, flow); +} + +static Polylines draw_perimeters(const ExPolygon &expoly, double clip_length) +{ + // Draw the perimeters. + Polylines polylines; + polylines.reserve(expoly.holes.size() + 1); + for (size_t i = 0; i <= expoly.holes.size(); ++ i) { + Polyline pl(i == 0 ? expoly.contour.points : expoly.holes[i - 1].points); + pl.points.emplace_back(pl.points.front()); + if (i > 0) + // It is a hole, reverse it. + pl.reverse(); + // so that all contours are CCW oriented. + pl.clip_end(clip_length); + polylines.emplace_back(std::move(pl)); + } + return polylines; +} + +static inline void tree_supports_generate_paths( + ExtrusionEntitiesPtr &dst, + const Polygons &polygons, + const Flow &flow) +{ + // Offset expolygon inside, returns number of expolygons collected (0 or 1). + // Vertices of output paths are marked with Z = source contour index of the expoly. + // Vertices at the intersection of source contours are marked with Z = -1. + auto shrink_expolygon_with_contour_idx = [](const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib_Z::Paths &out) -> int + { + assert(delta > 0); + auto append_paths_with_z = [](ClipperLib::Paths &src, coord_t contour_idx, ClipperLib_Z::Paths &dst) { + dst.reserve(next_highest_power_of_2(dst.size() + src.size())); + for (const ClipperLib::Path &contour : src) { + ClipperLib_Z::Path tmp; + tmp.reserve(contour.size()); + for (const Point &p : contour) + tmp.emplace_back(p.x(), p.y(), contour_idx); + dst.emplace_back(std::move(tmp)); + } + }; + + // 1) Offset the outer contour. + ClipperLib_Z::Paths contours; + { + ClipperLib::ClipperOffset co; + if (joinType == jtRound) + co.ArcTolerance = miterLimit; + else + co.MiterLimit = miterLimit; + co.ShortestEdgeLength = double(delta * 0.005); + co.AddPath(expoly.contour.points, joinType, ClipperLib::etClosedPolygon); + ClipperLib::Paths contours_raw; + co.Execute(contours_raw, - delta); + if (contours_raw.empty()) + // No need to try to offset the holes. + return 0; + append_paths_with_z(contours_raw, 0, contours); + } + + if (expoly.holes.empty()) { + // No need to subtract holes from the offsetted expolygon, we are done. + append(out, std::move(contours)); + } else { + // 2) Offset the holes one by one, collect the offsetted holes. + ClipperLib_Z::Paths holes; + { + for (const Polygon &hole : expoly.holes) { + ClipperLib::ClipperOffset co; + if (joinType == jtRound) + co.ArcTolerance = miterLimit; + else + co.MiterLimit = miterLimit; + co.ShortestEdgeLength = double(delta * 0.005); + co.AddPath(hole.points, joinType, ClipperLib::etClosedPolygon); + ClipperLib::Paths out2; + // Execute reorients the contours so that the outer most contour has a positive area. Thus the output + // contours will be CCW oriented even though the input paths are CW oriented. + // Offset is applied after contour reorientation, thus the signum of the offset value is reversed. + co.Execute(out2, delta); + append_paths_with_z(out2, 1 + (&hole - expoly.holes.data()), holes); + } + } + + // 3) Subtract holes from the contours. + if (holes.empty()) { + // No hole remaining after an offset. Just copy the outer contour. + append(out, std::move(contours)); + } else { + // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. + // Subtract the offsetted holes from the offsetted contours. + 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) { + //pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z())); + // Just mark the intersection. + pt.z() = -1; + }); + clipper.AddPaths(contours, ClipperLib_Z::ptSubject, true); + clipper.AddPaths(holes, ClipperLib_Z::ptClip, true); + ClipperLib_Z::Paths output; + clipper.Execute(ClipperLib_Z::ctDifference, output, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero); + if (! output.empty()) { + append(out, std::move(output)); + } else { + // The offsetted holes have eaten up the offsetted outer contour. + return 0; + } + } + } + + return 1; + }; + + const double spacing = flow.scaled_spacing(); + // Clip the sheath path to avoid the extruder to get exactly on the first point of the loop. + const double clip_length = spacing * 0.15; + const double anchor_length = spacing * 6.; + ClipperLib_Z::Paths anchor_candidates; + for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) { + std::unique_ptr eec; + double area = expoly.area(); + if (area > sqr(scaled(5.))) { + eec = std::make_unique(); + // Don't reoder internal / external loops of the same island, always start with the internal loop. + eec->no_sort = true; + // Make the tree branch stable by adding another perimeter. + ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); + if (level2.size() == 1) { + Polylines polylines; + extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + // Disable reversal of the path, always start with the anchor, always print CCW. + false); + expoly = level2.front(); + } + } + + // Try to produce one more perimeter to place the seam anchor. + // First genrate a 2nd perimeter loop as a source for anchor candidates. + // The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection. + anchor_candidates.clear(); + shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates); + // Orient all contours CW. + for (auto &path : anchor_candidates) + if (ClipperLib_Z::Area(path) > 0) + std::reverse(path.begin(), path.end()); + + // Draw the perimeters. + Polylines polylines; + polylines.reserve(expoly.holes.size() + 1); + for (size_t idx_loop = 0; idx_loop < expoly.num_contours(); ++ idx_loop) { + // Open the loop with a seam. + const Polygon &loop = expoly.contour_or_hole(idx_loop); + Polyline pl(loop.points); + // Orient all contours CW, because the anchor will be added to the end of polyline while we want to start a loop with the anchor. + if (idx_loop == 0) + // It is an outer contour. + pl.reverse(); + pl.points.emplace_back(pl.points.front()); + pl.clip_end(clip_length); + if (pl.size() < 2) + continue; + // Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour. + ClipperLib_Z::Path *closest_contour = nullptr; + Vec2d closest_point; + int closest_point_idx = -1; + double closest_point_t; + double d2min = std::numeric_limits::max(); + Vec2d seam_pt = pl.back().cast(); + for (ClipperLib_Z::Path &path : anchor_candidates) + for (int i = 0; i < path.size(); ++ i) { + int j = next_idx_modulo(i, path); + if (path[i].z() == idx_loop || path[j].z() == idx_loop) { + Vec2d pi(path[i].x(), path[i].y()); + Vec2d pj(path[j].x(), path[j].y()); + Vec2d v = pj - pi; + Vec2d w = seam_pt - pi; + auto l2 = v.squaredNorm(); + auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.); + if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) { + // Closest point. + Vec2d fp = pi + v * t; + double d2 = (fp - seam_pt).squaredNorm(); + if (d2 < d2min) { + d2min = d2; + closest_contour = &path; + closest_point = fp; + closest_point_idx = i; + closest_point_t = t; + } + } + } + } + if (d2min < sqr(flow.scaled_width() * 3.)) { + // Try to cut an anchor from the closest_contour. + // Both closest_contour and pl are CW oriented. + pl.points.emplace_back(closest_point.cast()); + const ClipperLib_Z::Path &path = *closest_contour; + double remaining_length = anchor_length - (seam_pt - closest_point).norm(); + int i = closest_point_idx; + int j = next_idx_modulo(i, *closest_contour); + Vec2d pi(path[i].x(), path[i].y()); + Vec2d pj(path[j].x(), path[j].y()); + Vec2d v = pj - pi; + double l = v.norm(); + if (remaining_length < (1. - closest_point_t) * l) { + // Just trim the current line. + pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast()); + } else { + // Take the rest of the current line, continue with the other lines. + pl.points.emplace_back(path[j].x(), path[j].y()); + pi = pj; + for (i = j; path[i].z() == idx_loop && remaining_length > 0; i = j, pi = pj) { + j = next_idx_modulo(i, path); + pj = Vec2d(path[j].x(), path[j].y()); + v = pj - pi; + l = v.norm(); + if (i == closest_point_idx) { + // Back at the first segment. Most likely this should not happen and we may end the anchor. + break; + } + if (remaining_length <= l) { + pl.points.emplace_back((pi + v * (remaining_length / l)).cast()); + break; + } + pl.points.emplace_back(path[j].x(), path[j].y()); + remaining_length -= l; + } + } + } + // Start with the anchor. + pl.reverse(); + polylines.emplace_back(std::move(pl)); + } + + ExtrusionEntitiesPtr &out = eec ? eec->entities : dst; + extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + // Disable reversal of the path, always start with the anchor, always print CCW. + false); + if (eec) { + std::reverse(eec->entities.begin(), eec->entities.end()); + dst.emplace_back(eec.release()); + } + } +} + +static inline void fill_expolygons_with_sheath_generate_paths( + ExtrusionEntitiesPtr &dst, + const Polygons &polygons, + Fill *filler, + float density, + ExtrusionRole role, + const Flow &flow, + bool with_sheath, + bool no_sort) +{ + if (polygons.empty()) + return; + + if (! with_sheath) { + fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow); + return; + } + + FillParams fill_params; + fill_params.density = density; + fill_params.dont_adjust = true; + + const double spacing = flow.scaled_spacing(); + // Clip the sheath path to avoid the extruder to get exactly on the first point of the loop. + const double clip_length = spacing * 0.15; + + for (ExPolygon &expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5*flow.scaled_width()))) { + // Don't reorder the skirt and its infills. + std::unique_ptr eec; + if (no_sort) { + eec = std::make_unique(); + eec->no_sort = true; + } + ExtrusionEntitiesPtr &out = no_sort ? eec->entities : dst; + extrusion_entities_append_paths(out, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); + // Fill in the rest. + fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); + if (no_sort && ! eec->empty()) + dst.emplace_back(eec.release()); + } +} + +// Support layers, partially processed. +struct SupportGeneratorLayerExtruded +{ + SupportGeneratorLayerExtruded& operator=(SupportGeneratorLayerExtruded &&rhs) { + this->layer = rhs.layer; + this->extrusions = std::move(rhs.extrusions); + m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude); + rhs.layer = nullptr; + return *this; + } + + bool empty() const { + return layer == nullptr || layer->polygons.empty(); + } + + void set_polygons_to_extrude(Polygons &&polygons) { + if (m_polygons_to_extrude == nullptr) + m_polygons_to_extrude = std::make_unique(std::move(polygons)); + else + *m_polygons_to_extrude = std::move(polygons); + } + Polygons& polygons_to_extrude() { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } + const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } + + bool could_merge(const SupportGeneratorLayerExtruded &other) const { + return ! this->empty() && ! other.empty() && + std::abs(this->layer->height - other.layer->height) < EPSILON && + this->layer->bridging == other.layer->bridging; + } + + // Merge regions, perform boolean union over the merged polygons. + void merge(SupportGeneratorLayerExtruded &&other) { + assert(this->could_merge(other)); + // 1) Merge the rest polygons to extrude, if there are any. + if (other.m_polygons_to_extrude != nullptr) { + if (m_polygons_to_extrude == nullptr) { + // This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). + assert(this->extrusions.empty()); + m_polygons_to_extrude = std::make_unique(this->layer->polygons); + } + Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude)); + *m_polygons_to_extrude = union_safety_offset(*m_polygons_to_extrude); + other.m_polygons_to_extrude.reset(); + } else if (m_polygons_to_extrude != nullptr) { + assert(other.m_polygons_to_extrude == nullptr); + // The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). + assert(other.extrusions.empty()); + Slic3r::polygons_append(*m_polygons_to_extrude, other.layer->polygons); + *m_polygons_to_extrude = union_safety_offset(*m_polygons_to_extrude); + } + // 2) Merge the extrusions. + this->extrusions.insert(this->extrusions.end(), other.extrusions.begin(), other.extrusions.end()); + other.extrusions.clear(); + // 3) Merge the infill polygons. + Slic3r::polygons_append(this->layer->polygons, std::move(other.layer->polygons)); + this->layer->polygons = union_safety_offset(this->layer->polygons); + other.layer->polygons.clear(); + } + + void polygons_append(Polygons &dst) const { + if (layer != NULL && ! layer->polygons.empty()) + Slic3r::polygons_append(dst, layer->polygons); + } + + // The source layer. It carries the height and extrusion type (bridging / non bridging, extrusion height). + SupportGeneratorLayer *layer { nullptr }; + // Collect extrusions. They will be exported sorted by the bottom height. + ExtrusionEntitiesPtr extrusions; + +private: + // In case the extrusions are non-empty, m_polygons_to_extrude may contain the rest areas yet to be filled by additional support. + // This is useful mainly for the loop interfaces, which are generated before the zig-zag infills. + std::unique_ptr m_polygons_to_extrude; +}; + +typedef std::vector SupportGeneratorLayerExtrudedPtrs; + +struct LoopInterfaceProcessor +{ + LoopInterfaceProcessor(coordf_t circle_r) : + n_contact_loops(0), + circle_radius(circle_r), + circle_distance(circle_r * 3.) + { + // Shape of the top contact area. + circle.points.reserve(6); + for (size_t i = 0; i < 6; ++ i) { + double angle = double(i) * M_PI / 3.; + circle.points.push_back(Point(circle_radius * cos(angle), circle_radius * sin(angle))); + } + } + + // Generate loop contacts at the top_contact_layer, + // trim the top_contact_layer->polygons with the areas covered by the loops. + void generate(SupportGeneratorLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const; + + int n_contact_loops; + coordf_t circle_radius; + coordf_t circle_distance; + Polygon circle; +}; + +void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const +{ + if (n_contact_loops == 0 || top_contact_layer.empty()) + return; + + Flow flow = interface_flow_src.with_height(top_contact_layer.layer->height); + + Polygons overhang_polygons; + if (top_contact_layer.layer->overhang_polygons != nullptr) + overhang_polygons = std::move(*top_contact_layer.layer->overhang_polygons); + + // Generate the outermost loop. + // Find centerline of the external loop (or any other kind of extrusions should the loop be skipped) + ExPolygons top_contact_expolygons = offset_ex(union_ex(top_contact_layer.layer->polygons), - 0.5f * flow.scaled_width()); + + // Grid size and bit shifts for quick and exact to/from grid coordinates manipulation. + coord_t circle_grid_resolution = 1; + coord_t circle_grid_powerof2 = 0; + { + // epsilon to account for rounding errors + coord_t circle_grid_resolution_non_powerof2 = coord_t(2. * circle_distance + 3.); + while (circle_grid_resolution < circle_grid_resolution_non_powerof2) { + circle_grid_resolution <<= 1; + ++ circle_grid_powerof2; + } + } + + struct PointAccessor { + const Point* operator()(const Point &pt) const { return &pt; } + }; + typedef ClosestPointInRadiusLookup ClosestPointLookupType; + + Polygons loops0; + { + // find centerline of the external loop of the contours + // Only consider the loops facing the overhang. + Polygons external_loops; + // Holes in the external loops. + Polygons circles; + Polygons overhang_with_margin = offset(union_ex(overhang_polygons), 0.5f * flow.scaled_width()); + for (ExPolygons::iterator it_contact_expoly = top_contact_expolygons.begin(); it_contact_expoly != top_contact_expolygons.end(); ++ it_contact_expoly) { + // Store the circle centers placed for an expolygon into a regular grid, hashed by the circle centers. + ClosestPointLookupType circle_centers_lookup(coord_t(circle_distance - SCALED_EPSILON)); + Points circle_centers; + Point center_last; + // For each contour of the expolygon, start with the outer contour, continue with the holes. + for (size_t i_contour = 0; i_contour <= it_contact_expoly->holes.size(); ++ i_contour) { + Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1]; + const Point *seg_current_pt = nullptr; + coordf_t seg_current_t = 0.; + if (! intersection_pl(contour.split_at_first_point(), overhang_with_margin).empty()) { + // The contour is below the overhang at least to some extent. + //FIXME ideally one would place the circles below the overhang only. + // Walk around the contour and place circles so their centers are not closer than circle_distance from each other. + if (circle_centers.empty()) { + // Place the first circle. + seg_current_pt = &contour.points.front(); + seg_current_t = 0.; + center_last = *seg_current_pt; + circle_centers_lookup.insert(center_last); + circle_centers.push_back(center_last); + } + for (Points::const_iterator it = contour.points.begin() + 1; it != contour.points.end(); ++it) { + // Is it possible to place a circle on this segment? Is it not too close to any of the circles already placed on this contour? + const Point &p1 = *(it-1); + const Point &p2 = *it; + // Intersection of a ray (p1, p2) with a circle placed at center_last, with radius of circle_distance. + const Vec2d v_seg(coordf_t(p2(0)) - coordf_t(p1(0)), coordf_t(p2(1)) - coordf_t(p1(1))); + const Vec2d v_cntr(coordf_t(p1(0) - center_last(0)), coordf_t(p1(1) - center_last(1))); + coordf_t a = v_seg.squaredNorm(); + coordf_t b = 2. * v_seg.dot(v_cntr); + coordf_t c = v_cntr.squaredNorm() - circle_distance * circle_distance; + coordf_t disc = b * b - 4. * a * c; + if (disc > 0.) { + // The circle intersects a ray. Avoid the parts of the segment inside the circle. + coordf_t t1 = (-b - sqrt(disc)) / (2. * a); + coordf_t t2 = (-b + sqrt(disc)) / (2. * a); + coordf_t t0 = (seg_current_pt == &p1) ? seg_current_t : 0.; + // Take the lowest t in , excluding . + coordf_t t; + if (t0 <= t1) + t = t0; + else if (t2 <= 1.) + t = t2; + else { + // Try the following segment. + seg_current_pt = nullptr; + continue; + } + seg_current_pt = &p1; + seg_current_t = t; + center_last = Point(p1(0) + coord_t(v_seg(0) * t), p1(1) + coord_t(v_seg(1) * t)); + // It has been verified that the new point is far enough from center_last. + // Ensure, that it is far enough from all the centers. + std::pair circle_closest = circle_centers_lookup.find(center_last); + if (circle_closest.first != nullptr) { + -- it; + continue; + } + } else { + // All of the segment is outside the circle. Take the first point. + seg_current_pt = &p1; + seg_current_t = 0.; + center_last = p1; + } + // Place the first circle. + circle_centers_lookup.insert(center_last); + circle_centers.push_back(center_last); + } + external_loops.push_back(std::move(contour)); + for (const Point ¢er : circle_centers) { + circles.push_back(circle); + circles.back().translate(center); + } + } + } + } + // Apply a pattern to the external loops. + loops0 = diff(external_loops, circles); + } + + Polylines loop_lines; + { + // make more loops + Polygons loop_polygons = loops0; + for (int i = 1; i < n_contact_loops; ++ i) + polygons_append(loop_polygons, + opening( + loops0, + i * flow.scaled_spacing() + 0.5f * flow.scaled_spacing(), + 0.5f * flow.scaled_spacing())); + // Clip such loops to the side oriented towards the object. + // Collect split points, so they will be recognized after the clipping. + // At the split points the clipped pieces will be stitched back together. + loop_lines.reserve(loop_polygons.size()); + std::unordered_map map_split_points; + for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it) { + assert(map_split_points.find(it->first_point()) == map_split_points.end()); + map_split_points[it->first_point()] = -1; + loop_lines.push_back(it->split_at_first_point()); + } + loop_lines = intersection_pl(loop_lines, expand(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN))); + // Because a closed loop has been split to a line, loop_lines may contain continuous segments split to 2 pieces. + // Try to connect them. + for (int i_line = 0; i_line < int(loop_lines.size()); ++ i_line) { + Polyline &polyline = loop_lines[i_line]; + auto it = map_split_points.find(polyline.first_point()); + if (it != map_split_points.end()) { + // This is a stitching point. + // If this assert triggers, multiple source polygons likely intersected at this point. + assert(it->second != -2); + if (it->second < 0) { + // First occurence. + it->second = i_line; + } else { + // Second occurence. Join the lines. + Polyline &polyline_1st = loop_lines[it->second]; + assert(polyline_1st.first_point() == it->first || polyline_1st.last_point() == it->first); + if (polyline_1st.first_point() == it->first) + polyline_1st.reverse(); + polyline_1st.append(std::move(polyline)); + it->second = -2; + } + continue; + } + it = map_split_points.find(polyline.last_point()); + if (it != map_split_points.end()) { + // This is a stitching point. + // If this assert triggers, multiple source polygons likely intersected at this point. + assert(it->second != -2); + if (it->second < 0) { + // First occurence. + it->second = i_line; + } else { + // Second occurence. Join the lines. + Polyline &polyline_1st = loop_lines[it->second]; + assert(polyline_1st.first_point() == it->first || polyline_1st.last_point() == it->first); + if (polyline_1st.first_point() == it->first) + polyline_1st.reverse(); + polyline.reverse(); + polyline_1st.append(std::move(polyline)); + it->second = -2; + } + } + } + // Remove empty lines. + remove_degenerate(loop_lines); + } + + // add the contact infill area to the interface area + // note that growing loops by $circle_radius ensures no tiny + // extrusions are left inside the circles; however it creates + // a very large gap between loops and contact_infill_polygons, so maybe another + // solution should be found to achieve both goals + // Store the trimmed polygons into a separate polygon set, so the original infill area remains intact for + // "modulate by layer thickness". + top_contact_layer.set_polygons_to_extrude(diff(top_contact_layer.layer->polygons, offset(loop_lines, float(circle_radius * 1.1)))); + + // Transform loops into ExtrusionPath objects. + extrusion_entities_append_paths( + top_contact_layer.extrusions, + std::move(loop_lines), + ExtrusionRole::SupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); +} + +#ifdef SLIC3R_DEBUG +static std::string dbg_index_to_color(int idx) +{ + if (idx < 0) + return "yellow"; + idx = idx % 3; + switch (idx) { + case 0: return "red"; + case 1: return "green"; + default: return "blue"; + } +} +#endif /* SLIC3R_DEBUG */ + +// When extruding a bottom interface layer over an object, the bottom interface layer is extruded in a thin air, therefore +// it is being extruded with a bridging flow to not shrink excessively (the die swell effect). +// Tiny extrusions are better avoided and it is always better to anchor the thread to an existing support structure if possible. +// Therefore the bottom interface spots are expanded a bit. The expanded regions may overlap with another bottom interface layers, +// leading to over extrusion, where they overlap. The over extrusion is better avoided as it often makes the interface layers +// to stick too firmly to the object. +// +// Modulate thickness (increase bottom_z) of extrusions_in_out generated for this_layer +// if they overlap with overlapping_layers, whose print_z is above this_layer.bottom_z() and below this_layer.print_z. +static void modulate_extrusion_by_overlapping_layers( + // Extrusions generated for this_layer. + ExtrusionEntitiesPtr &extrusions_in_out, + const SupportGeneratorLayer &this_layer, + // Multiple layers overlapping with this_layer, sorted bottom up. + const SupportGeneratorLayersPtr &overlapping_layers) +{ + size_t n_overlapping_layers = overlapping_layers.size(); + if (n_overlapping_layers == 0 || extrusions_in_out.empty()) + // The extrusions do not overlap with any other extrusion. + return; + + // Get the initial extrusion parameters. + ExtrusionPath *extrusion_path_template = dynamic_cast(extrusions_in_out.front()); + assert(extrusion_path_template != nullptr); + ExtrusionRole extrusion_role = extrusion_path_template->role(); + float extrusion_width = extrusion_path_template->width; + + struct ExtrusionPathFragment + { + ExtrusionPathFragment() : mm3_per_mm(-1), width(-1), height(-1) {}; + ExtrusionPathFragment(double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height) {}; + + Polylines polylines; + double mm3_per_mm; + float width; + float height; + }; + + // Split the extrusions by the overlapping layers, reduce their extrusion rate. + // The last path_fragment is from this_layer. + std::vector path_fragments( + n_overlapping_layers + 1, + ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height)); + // Don't use it, it will be released. + extrusion_path_template = nullptr; + +#ifdef SLIC3R_DEBUG + static int iRun = 0; + ++ iRun; + BoundingBox bbox; + for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { + const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; + bbox.merge(get_extents(overlapping_layer.polygons)); + } + for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { + ExtrusionPath *path = dynamic_cast(*it); + assert(path != nullptr); + bbox.merge(get_extents(path->polyline)); + } + SVG svg(debug_out_path("support-fragments-%d-%lf.svg", iRun, this_layer.print_z).c_str(), bbox); + const float transparency = 0.5f; + // Filled polygons for the overlapping regions. + svg.draw(union_ex(this_layer.polygons), dbg_index_to_color(-1), transparency); + for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { + const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; + svg.draw(union_ex(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), transparency); + } + // Contours of the overlapping regions. + svg.draw(to_polylines(this_layer.polygons), dbg_index_to_color(-1), scale_(0.2)); + for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { + const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; + svg.draw(to_polylines(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), scale_(0.1)); + } + // Fill extrusion, the source. + for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { + ExtrusionPath *path = dynamic_cast(*it); + std::string color_name; + switch ((it - extrusions_in_out.begin()) % 9) { + case 0: color_name = "magenta"; break; + case 1: color_name = "deepskyblue"; break; + case 2: color_name = "coral"; break; + case 3: color_name = "goldenrod"; break; + case 4: color_name = "orange"; break; + case 5: color_name = "olivedrab"; break; + case 6: color_name = "blueviolet"; break; + case 7: color_name = "brown"; break; + default: color_name = "orchid"; break; + } + svg.draw(path->polyline, color_name, scale_(0.2)); + } +#endif /* SLIC3R_DEBUG */ + + // End points of the original paths. + std::vector> path_ends; + // Collect the paths of this_layer. + { + Polylines &polylines = path_fragments.back().polylines; + for (ExtrusionEntity *ee : extrusions_in_out) { + ExtrusionPath *path = dynamic_cast(ee); + assert(path != nullptr); + polylines.emplace_back(Polyline(std::move(path->polyline))); + path_ends.emplace_back(std::pair(polylines.back().points.front(), polylines.back().points.back())); + delete path; + } + } + // Destroy the original extrusion paths, their polylines were moved to path_fragments already. + // This will be the destination for the new paths. + extrusions_in_out.clear(); + + // Fragment the path segments by overlapping layers. The overlapping layers are sorted by an increasing print_z. + // Trim by the highest overlapping layer first. + for (int i_overlapping_layer = int(n_overlapping_layers) - 1; i_overlapping_layer >= 0; -- i_overlapping_layer) { + const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; + ExtrusionPathFragment &frag = path_fragments[i_overlapping_layer]; + Polygons polygons_trimming = offset(union_ex(overlapping_layer.polygons), float(scale_(0.5*extrusion_width))); + frag.polylines = intersection_pl(path_fragments.back().polylines, polygons_trimming); + path_fragments.back().polylines = diff_pl(path_fragments.back().polylines, polygons_trimming); + // Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter). + assert(this_layer.print_z > overlapping_layer.print_z); + frag.height = float(this_layer.print_z - overlapping_layer.print_z); + frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f).mm3_per_mm(); +#ifdef SLIC3R_DEBUG + svg.draw(frag.polylines, dbg_index_to_color(i_overlapping_layer), scale_(0.1)); +#endif /* SLIC3R_DEBUG */ + } + +#ifdef SLIC3R_DEBUG + svg.draw(path_fragments.back().polylines, dbg_index_to_color(-1), scale_(0.1)); + svg.Close(); +#endif /* SLIC3R_DEBUG */ + + // Now chain the split segments using hashing and a nearly exact match, maintaining the order of segments. + // Create a single ExtrusionPath or ExtrusionEntityCollection per source ExtrusionPath. + // Map of fragment start/end points to a pair of + // Because a non-exact matching is used for the end points, a multi-map is used. + // As the clipper library may reverse the order of some clipped paths, store both ends into the map. + struct ExtrusionPathFragmentEnd + { + ExtrusionPathFragmentEnd(size_t alayer_idx, size_t apolyline_idx, bool ais_start) : + layer_idx(alayer_idx), polyline_idx(apolyline_idx), is_start(ais_start) {} + size_t layer_idx; + size_t polyline_idx; + bool is_start; + }; + class ExtrusionPathFragmentEndPointAccessor { + public: + ExtrusionPathFragmentEndPointAccessor(const std::vector &path_fragments) : m_path_fragments(path_fragments) {} + // Return an end point of a fragment, or nullptr if the fragment has been consumed already. + const Point* operator()(const ExtrusionPathFragmentEnd &fragment_end) const { + const Polyline &polyline = m_path_fragments[fragment_end.layer_idx].polylines[fragment_end.polyline_idx]; + return polyline.points.empty() ? nullptr : + (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back()); + } + private: + ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) { + return *this; + } + + const std::vector &m_path_fragments; + }; + const coord_t search_radius = 7; + ClosestPointInRadiusLookup map_fragment_starts( + search_radius, ExtrusionPathFragmentEndPointAccessor(path_fragments)); + for (size_t i_overlapping_layer = 0; i_overlapping_layer <= n_overlapping_layers; ++ i_overlapping_layer) { + const Polylines &polylines = path_fragments[i_overlapping_layer].polylines; + for (size_t i_polyline = 0; i_polyline < polylines.size(); ++ i_polyline) { + // Map a starting point of a polyline to a pair of + if (polylines[i_polyline].points.size() >= 2) { + map_fragment_starts.insert(ExtrusionPathFragmentEnd(i_overlapping_layer, i_polyline, true)); + map_fragment_starts.insert(ExtrusionPathFragmentEnd(i_overlapping_layer, i_polyline, false)); + } + } + } + + // For each source path: + for (size_t i_path = 0; i_path < path_ends.size(); ++ i_path) { + const Point &pt_start = path_ends[i_path].first; + const Point &pt_end = path_ends[i_path].second; + Point pt_current = pt_start; + // Find a chain of fragments with the original / reduced print height. + ExtrusionMultiPath multipath; + for (;;) { + // Find a closest end point to pt_current. + std::pair end_and_dist2 = map_fragment_starts.find(pt_current); + // There may be a bug in Clipper flipping the order of two last points in a fragment? + // assert(end_and_dist2.first != nullptr); + assert(end_and_dist2.first == nullptr || end_and_dist2.second < search_radius * search_radius); + if (end_and_dist2.first == nullptr) { + // New fragment connecting to pt_current was not found. + // Verify that the last point found is close to the original end point of the unfragmented path. + //const double d2 = (pt_end - pt_current).cast.squaredNorm(); + //assert(d2 < coordf_t(search_radius * search_radius)); + // End of the path. + break; + } + const ExtrusionPathFragmentEnd &fragment_end_min = *end_and_dist2.first; + // Fragment to consume. + ExtrusionPathFragment &frag = path_fragments[fragment_end_min.layer_idx]; + Polyline &frag_polyline = frag.polylines[fragment_end_min.polyline_idx]; + // Path to append the fragment to. + ExtrusionPath *path = multipath.paths.empty() ? nullptr : &multipath.paths.back(); + if (path != nullptr) { + // Verify whether the path is compatible with the current fragment. + assert(this_layer.layer_type == SupporLayerType::BottomContact || path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm); + if (path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm) { + path = nullptr; + } + // Merging with the previous path. This can only happen if the current layer was reduced by a base layer, which was split into a base and interface layer. + } + if (path == nullptr) { + // Allocate a new path. + multipath.paths.push_back(ExtrusionPath(extrusion_role, frag.mm3_per_mm, frag.width, frag.height)); + path = &multipath.paths.back(); + } + // The Clipper library may flip the order of the clipped polylines arbitrarily. + // Reverse the source polyline, if connecting to the end. + if (! fragment_end_min.is_start) + frag_polyline.reverse(); + // Enforce exact overlap of the end points of successive fragments. + assert(frag_polyline.points.front() == pt_current); + frag_polyline.points.front() = pt_current; + // Don't repeat the first point. + if (! path->polyline.points.empty()) + path->polyline.points.pop_back(); + // Consume the fragment's polyline, remove it from the input fragments, so it will be ignored the next time. + path->polyline.append(std::move(frag_polyline)); + frag_polyline.points.clear(); + pt_current = path->polyline.points.back(); + if (pt_current == pt_end) { + // End of the path. + break; + } + } + if (!multipath.paths.empty()) { + if (multipath.paths.size() == 1) { + // This path was not fragmented. + extrusions_in_out.push_back(new ExtrusionPath(std::move(multipath.paths.front()))); + } else { + // This path was fragmented. Copy the collection as a whole object, so the order inside the collection will not be changed + // during the chaining of extrusions_in_out. + extrusions_in_out.push_back(new ExtrusionMultiPath(std::move(multipath))); + } + } + } + // If there are any non-consumed fragments, add them separately. + //FIXME this shall not happen, if the Clipper works as expected and all paths split to fragments could be re-connected. + for (auto it_fragment = path_fragments.begin(); it_fragment != path_fragments.end(); ++ it_fragment) + extrusion_entities_append_paths(extrusions_in_out, std::move(it_fragment->polylines), extrusion_role, it_fragment->mm3_per_mm, it_fragment->width, it_fragment->height); +} + +// Support layer that is covered by some form of dense interface. +static constexpr const std::initializer_list support_types_interface{ + SupporLayerType::RaftInterface, SupporLayerType::BottomContact, SupporLayerType::BottomInterface, SupporLayerType::TopContact, SupporLayerType::TopInterface +}; + +SupportGeneratorLayersPtr generate_support_layers( + PrintObject &object, + const SupportGeneratorLayersPtr &raft_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers) +{ + // Install support layers into the object. + // A support layer installed on a PrintObject has a unique print_z. + SupportGeneratorLayersPtr layers_sorted; + layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size() + base_interface_layers.size()); + append(layers_sorted, raft_layers); + append(layers_sorted, bottom_contacts); + append(layers_sorted, top_contacts); + append(layers_sorted, intermediate_layers); + append(layers_sorted, interface_layers); + append(layers_sorted, base_interface_layers); + // Sort the layers lexicographically by a raising print_z and a decreasing height. + std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); + int layer_id = 0; + int layer_id_interface = 0; + assert(object.support_layers().empty()); + for (size_t i = 0; i < layers_sorted.size();) { + // Find the last layer with roughly the same print_z, find the minimum layer height of all. + // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. + size_t j = i + 1; + coordf_t zmax = layers_sorted[i]->print_z + EPSILON; + for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ; + // Assign an average print_z to the set of layers with nearly equal print_z. + coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z); + coordf_t height_min = layers_sorted[i]->height; + bool empty = true; + // For snug supports, layers where the direction of the support interface shall change are accounted for. + size_t num_interfaces = 0; + size_t num_top_contacts = 0; + double top_contact_bottom_z = 0; + for (size_t u = i; u < j; ++u) { + SupportGeneratorLayer &layer = *layers_sorted[u]; + if (! layer.polygons.empty()) { + empty = false; + num_interfaces += one_of(layer.layer_type, support_types_interface); + if (layer.layer_type == SupporLayerType::TopContact) { + ++ num_top_contacts; + assert(num_top_contacts <= 1); + // All top contact layers sharing this print_z shall also share bottom_z. + //assert(num_top_contacts == 1 || (top_contact_bottom_z - layer.bottom_z) < EPSILON); + top_contact_bottom_z = layer.bottom_z; + } + } + layer.print_z = zavg; + height_min = std::min(height_min, layer.height); + } + if (! empty) { + // Here the upper_layer and lower_layer pointers are left to null at the support layers, + // as they are never used. These pointers are candidates for removal. + bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; + size_t this_layer_id_interface = layer_id_interface; + if (this_layer_contacts_only) { + // Find a supporting layer for its interface ID. + for (auto it = object.support_layers().rbegin(); it != object.support_layers().rend(); ++ it) + if (const SupportLayer &other_layer = **it; std::abs(other_layer.print_z - top_contact_bottom_z) < EPSILON) { + // other_layer supports this top contact layer. Assign a different support interface direction to this layer + // from the layer that supports it. + this_layer_id_interface = other_layer.interface_id() + 1; + } + } + object.add_support_layer(layer_id ++, this_layer_id_interface, height_min, zavg); + if (num_interfaces && ! this_layer_contacts_only) + ++ layer_id_interface; + } + i = j; + } + return layers_sorted; +} + +void generate_support_toolpaths( + SupportLayerPtrs &support_layers, + const PrintObjectConfig &config, + const SupportParameters &support_params, + const SlicingParameters &slicing_params, + const SupportGeneratorLayersPtr &raft_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers) +{ + // loop_interface_processor with a given circle radius. + LoopInterfaceProcessor loop_interface_processor(1.5 * support_params.support_material_interface_flow.scaled_width()); + loop_interface_processor.n_contact_loops = config.support_material_interface_contact_loops ? 1 : 0; + + std::vector angles { support_params.base_angle }; + if (config.support_material_pattern == smpRectilinearGrid) + angles.push_back(support_params.interface_angle); + + BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); + +// const coordf_t link_max_length_factor = 3.; + const coordf_t link_max_length_factor = 0.; + + // Insert the raft base layers. + auto n_raft_layers = std::min(support_layers.size(), std::max(0, int(slicing_params.raft_layers()) - 1)); + + tbb::parallel_for(tbb::blocked_range(0, n_raft_layers), + [&support_layers, &raft_layers, &intermediate_layers, &config, &support_params, &slicing_params, + &bbox_object, link_max_length_factor] + (const tbb::blocked_range& range) { + for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) + { + assert(support_layer_id < raft_layers.size()); + SupportLayer &support_layer = *support_layers[support_layer_id]; + assert(support_layer.support_fills.entities.empty()); + SupportGeneratorLayer &raft_layer = *raft_layers[support_layer_id]; + + std::unique_ptr filler_interface = std::unique_ptr(Fill::new_from_type(support_params.raft_interface_fill_pattern)); + std::unique_ptr filler_support = std::unique_ptr(Fill::new_from_type(support_params.base_fill_pattern)); + filler_interface->set_bounding_box(bbox_object); + filler_support->set_bounding_box(bbox_object); + + // Print the tree supports cutting through the raft with the exception of the 1st layer, where a full support layer will be printed below + // both the raft and the trees. + // Trim the raft layers with the tree polygons. + const Polygons &tree_polygons = + support_layer_id > 0 && support_layer_id < intermediate_layers.size() && is_approx(intermediate_layers[support_layer_id]->print_z, support_layer.print_z) ? + intermediate_layers[support_layer_id]->polygons : Polygons(); + + // Print the support base below the support columns, or the support base for the support columns plus the contacts. + if (support_layer_id > 0) { + const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? + raft_layer.polygons : + //FIXME misusing contact_polygons for support columns. + ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); + // Trees may cut through the raft layers down to a print bed. + Flow flow(float(support_params.support_material_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter()); + assert(!raft_layer.bridging); + if (! to_infill_polygons.empty()) { + Fill *filler = filler_support.get(); + filler->angle = support_params.raft_angle_base; + filler->spacing = support_params.support_material_flow.spacing(); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density)); + fill_expolygons_with_sheath_generate_paths( + // Destination + support_layer.support_fills.entities, + // Regions to fill + tree_polygons.empty() ? to_infill_polygons : diff(to_infill_polygons, tree_polygons), + // Filler and its parameters + filler, float(support_params.support_density), + // Extrusion parameters + ExtrusionRole::SupportMaterial, flow, + support_params.with_sheath, false); + } + if (! tree_polygons.empty()) + tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow); + } + + Fill *filler = filler_interface.get(); + Flow flow = support_params.first_layer_flow; + float density = 0.f; + if (support_layer_id == 0) { + // Base flange. + filler->angle = support_params.raft_angle_1st_layer; + filler->spacing = support_params.first_layer_flow.spacing(); + density = float(config.raft_first_layer_density.value * 0.01); + } else if (support_layer_id >= slicing_params.base_raft_layers) { + filler->angle = support_params.raft_interface_angle(support_layer.interface_id()); + // We don't use $base_flow->spacing because we need a constant spacing + // value that guarantees that all layers are correctly aligned. + filler->spacing = support_params.support_material_flow.spacing(); + assert(! raft_layer.bridging); + flow = Flow(float(support_params.raft_interface_flow.width()), float(raft_layer.height), support_params.raft_interface_flow.nozzle_diameter()); + density = float(support_params.raft_interface_density); + } else + continue; + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); + fill_expolygons_with_sheath_generate_paths( + // Destination + support_layer.support_fills.entities, + // Regions to fill + tree_polygons.empty() ? raft_layer.polygons : diff(raft_layer.polygons, tree_polygons), + // Filler and its parameters + filler, density, + // Extrusion parameters + (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface, flow, + // sheath at first layer + support_layer_id == 0, support_layer_id == 0); + } + }); + + struct LayerCacheItem { + LayerCacheItem(SupportGeneratorLayerExtruded *layer_extruded = nullptr) : layer_extruded(layer_extruded) {} + SupportGeneratorLayerExtruded *layer_extruded; + std::vector overlapping; + }; + struct LayerCache { + SupportGeneratorLayerExtruded bottom_contact_layer; + SupportGeneratorLayerExtruded top_contact_layer; + SupportGeneratorLayerExtruded base_layer; + SupportGeneratorLayerExtruded interface_layer; + SupportGeneratorLayerExtruded base_interface_layer; + boost::container::static_vector nonempty; + + void add_nonempty_and_sort() { + for (SupportGeneratorLayerExtruded *item : { &bottom_contact_layer, &top_contact_layer, &interface_layer, &base_interface_layer, &base_layer }) + if (! item->empty()) + this->nonempty.emplace_back(item); + // Sort the layers with the same print_z coordinate by their heights, thickest first. + std::stable_sort(this->nonempty.begin(), this->nonempty.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; }); + } + }; + std::vector layer_caches(support_layers.size()); + + tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), + [&config, &slicing_params, &support_params, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &base_interface_layers, &layer_caches, &loop_interface_processor, + &bbox_object, &angles, n_raft_layers, link_max_length_factor] + (const tbb::blocked_range& range) { + // Indices of the 1st layer in their respective container at the support layer height. + size_t idx_layer_bottom_contact = size_t(-1); + size_t idx_layer_top_contact = size_t(-1); + size_t idx_layer_intermediate = size_t(-1); + size_t idx_layer_interface = size_t(-1); + size_t idx_layer_base_interface = size_t(-1); + const auto fill_type_first_layer = ipRectilinear; + auto filler_interface = std::unique_ptr(Fill::new_from_type(support_params.contact_fill_pattern)); + // Filler for the 1st layer interface, if different from filler_interface. + auto filler_first_layer_ptr = std::unique_ptr(range.begin() == 0 && support_params.contact_fill_pattern != fill_type_first_layer ? Fill::new_from_type(fill_type_first_layer) : nullptr); + // Pointer to the 1st layer interface filler. + auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get(); + // Filler for the 1st layer interface, if different from filler_interface. + auto filler_raft_contact_ptr = std::unique_ptr(range.begin() == n_raft_layers && config.support_material_interface_layers.value == 0 ? + Fill::new_from_type(support_params.raft_interface_fill_pattern) : nullptr); + // Pointer to the 1st layer interface filler. + auto filler_raft_contact = filler_raft_contact_ptr ? filler_raft_contact_ptr.get() : filler_interface.get(); + // Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer). + auto filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : + Fill::new_from_type(support_params.interface_density > 0.95 || support_params.with_sheath ? ipRectilinear : ipSupportBase)); + auto filler_support = std::unique_ptr(Fill::new_from_type(support_params.base_fill_pattern)); + filler_interface->set_bounding_box(bbox_object); + if (filler_first_layer_ptr) + filler_first_layer_ptr->set_bounding_box(bbox_object); + if (filler_raft_contact_ptr) + filler_raft_contact_ptr->set_bounding_box(bbox_object); + if (filler_base_interface) + filler_base_interface->set_bounding_box(bbox_object); + filler_support->set_bounding_box(bbox_object); + for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) + { + SupportLayer &support_layer = *support_layers[support_layer_id]; + LayerCache &layer_cache = layer_caches[support_layer_id]; + const float support_interface_angle = config.support_material_style.value == smsGrid ? + support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id()); + + // Find polygons with the same print_z. + SupportGeneratorLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer; + SupportGeneratorLayerExtruded &top_contact_layer = layer_cache.top_contact_layer; + SupportGeneratorLayerExtruded &base_layer = layer_cache.base_layer; + SupportGeneratorLayerExtruded &interface_layer = layer_cache.interface_layer; + SupportGeneratorLayerExtruded &base_interface_layer = layer_cache.base_interface_layer; + // Increment the layer indices to find a layer at support_layer.print_z. + { + auto fun = [&support_layer](const SupportGeneratorLayer *l){ return l->print_z >= support_layer.print_z - EPSILON; }; + idx_layer_bottom_contact = idx_higher_or_equal(bottom_contacts, idx_layer_bottom_contact, fun); + idx_layer_top_contact = idx_higher_or_equal(top_contacts, idx_layer_top_contact, fun); + idx_layer_intermediate = idx_higher_or_equal(intermediate_layers, idx_layer_intermediate, fun); + idx_layer_interface = idx_higher_or_equal(interface_layers, idx_layer_interface, fun); + idx_layer_base_interface = idx_higher_or_equal(base_interface_layers, idx_layer_base_interface,fun); + } + // Copy polygons from the layers. + if (idx_layer_bottom_contact < bottom_contacts.size() && bottom_contacts[idx_layer_bottom_contact]->print_z < support_layer.print_z + EPSILON) + bottom_contact_layer.layer = bottom_contacts[idx_layer_bottom_contact]; + if (idx_layer_top_contact < top_contacts.size() && top_contacts[idx_layer_top_contact]->print_z < support_layer.print_z + EPSILON) + top_contact_layer.layer = top_contacts[idx_layer_top_contact]; + if (idx_layer_interface < interface_layers.size() && interface_layers[idx_layer_interface]->print_z < support_layer.print_z + EPSILON) + interface_layer.layer = interface_layers[idx_layer_interface]; + if (idx_layer_base_interface < base_interface_layers.size() && base_interface_layers[idx_layer_base_interface]->print_z < support_layer.print_z + EPSILON) + base_interface_layer.layer = base_interface_layers[idx_layer_base_interface]; + if (idx_layer_intermediate < intermediate_layers.size() && intermediate_layers[idx_layer_intermediate]->print_z < support_layer.print_z + EPSILON) + base_layer.layer = intermediate_layers[idx_layer_intermediate]; + + // This layer is a raft contact layer. Any contact polygons at this layer are raft contacts. + bool raft_layer = slicing_params.interface_raft_layers && top_contact_layer.layer && is_approx(top_contact_layer.layer->print_z, slicing_params.raft_contact_top_z); + if (config.support_material_interface_layers == 0) { + // If no top interface layers were requested, we treat the contact layer exactly as a generic base layer. + // Don't merge the raft contact layer though. + if (support_params.can_merge_support_regions && ! raft_layer) { + if (base_layer.could_merge(top_contact_layer)) + base_layer.merge(std::move(top_contact_layer)); + else if (base_layer.empty()) + base_layer = std::move(top_contact_layer); + } + } else { + loop_interface_processor.generate(top_contact_layer, support_params.support_material_interface_flow); + // If no loops are allowed, we treat the contact layer exactly as a generic interface layer. + // Merge interface_layer into top_contact_layer, as the top_contact_layer is not synchronized and therefore it will be used + // to trim other layers. + if (top_contact_layer.could_merge(interface_layer) && ! raft_layer) + top_contact_layer.merge(std::move(interface_layer)); + } + if ((config.support_material_interface_layers == 0 || config.support_material_bottom_interface_layers == 0) && support_params.can_merge_support_regions) { + if (base_layer.could_merge(bottom_contact_layer)) + base_layer.merge(std::move(bottom_contact_layer)); + else if (base_layer.empty() && ! bottom_contact_layer.empty() && ! bottom_contact_layer.layer->bridging) + base_layer = std::move(bottom_contact_layer); + } else if (bottom_contact_layer.could_merge(top_contact_layer) && ! raft_layer) + top_contact_layer.merge(std::move(bottom_contact_layer)); + else if (bottom_contact_layer.could_merge(interface_layer)) + bottom_contact_layer.merge(std::move(interface_layer)); + +#if 0 + if ( ! interface_layer.empty() && ! base_layer.empty()) { + // turn base support into interface when it's contained in our holes + // (this way we get wider interface anchoring) + //FIXME The intention of the code below is unclear. One likely wanted to just merge small islands of base layers filling in the holes + // inside interface layers, but the code below fills just too much, see GH #4570 + Polygons islands = top_level_islands(interface_layer.layer->polygons); + polygons_append(interface_layer.layer->polygons, intersection(base_layer.layer->polygons, islands)); + base_layer.layer->polygons = diff(base_layer.layer->polygons, islands); + } +#endif + + // Top and bottom contacts, interface layers. + enum class InterfaceLayerType { TopContact, BottomContact, RaftContact, Interface, InterfaceAsBase }; + auto extrude_interface = [&](SupportGeneratorLayerExtruded &layer_ex, InterfaceLayerType interface_layer_type) { + if (! layer_ex.empty() && ! layer_ex.polygons_to_extrude().empty()) { + bool interface_as_base = interface_layer_type == InterfaceLayerType::InterfaceAsBase; + bool raft_contact = interface_layer_type == InterfaceLayerType::RaftContact; + //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore + // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) + auto *filler = raft_contact ? filler_raft_contact : filler_interface.get(); + auto interface_flow = layer_ex.layer->bridging ? + Flow::bridging_flow(layer_ex.layer->height, support_params.support_material_bottom_interface_flow.nozzle_diameter()) : + (raft_contact ? &support_params.raft_interface_flow : + interface_as_base ? &support_params.support_material_flow : &support_params.support_material_interface_flow) + ->with_height(float(layer_ex.layer->height)); + filler->angle = interface_as_base ? + // If zero interface layers are configured, use the same angle as for the base layers. + angles[support_layer_id % angles.size()] : + // Use interface angle for the interface layers. + raft_contact ? + support_params.raft_interface_angle(support_layer.interface_id()) : + support_interface_angle; + double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density; + filler->spacing = raft_contact ? support_params.raft_interface_flow.spacing() : + interface_as_base ? support_params.support_material_flow.spacing() : support_params.support_material_interface_flow.spacing(); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); + fill_expolygons_generate_paths( + // Destination + layer_ex.extrusions, + // Regions to fill + union_safety_offset_ex(layer_ex.polygons_to_extrude()), + // Filler and its parameters + filler, float(density), + // Extrusion parameters + ExtrusionRole::SupportMaterialInterface, interface_flow); + } + }; + const bool top_interfaces = config.support_material_interface_layers.value != 0; + const bool bottom_interfaces = top_interfaces && config.support_material_bottom_interface_layers != 0; + extrude_interface(top_contact_layer, raft_layer ? InterfaceLayerType::RaftContact : top_interfaces ? InterfaceLayerType::TopContact : InterfaceLayerType::InterfaceAsBase); + extrude_interface(bottom_contact_layer, bottom_interfaces ? InterfaceLayerType::BottomContact : InterfaceLayerType::InterfaceAsBase); + extrude_interface(interface_layer, top_interfaces ? InterfaceLayerType::Interface : InterfaceLayerType::InterfaceAsBase); + + // Base interface layers under soluble interfaces + if ( ! base_interface_layer.empty() && ! base_interface_layer.polygons_to_extrude().empty()) { + Fill *filler = filler_base_interface.get(); + //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore + // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) + assert(! base_interface_layer.layer->bridging); + Flow interface_flow = support_params.support_material_flow.with_height(float(base_interface_layer.layer->height)); + filler->angle = support_interface_angle; + filler->spacing = support_params.support_material_interface_flow.spacing(); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.interface_density)); + fill_expolygons_generate_paths( + // Destination + base_interface_layer.extrusions, + //base_layer_interface.extrusions, + // Regions to fill + union_safety_offset_ex(base_interface_layer.polygons_to_extrude()), + // Filler and its parameters + filler, float(support_params.interface_density), + // Extrusion parameters + ExtrusionRole::SupportMaterial, interface_flow); + } + + // Base support or flange. + if (! base_layer.empty() && ! base_layer.polygons_to_extrude().empty()) { + Fill *filler = filler_support.get(); + filler->angle = angles[support_layer_id % angles.size()]; + // We don't use $base_flow->spacing because we need a constant spacing + // value that guarantees that all layers are correctly aligned. + assert(! base_layer.layer->bridging); + auto flow = support_params.support_material_flow.with_height(float(base_layer.layer->height)); + filler->spacing = support_params.support_material_flow.spacing(); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density)); + float density = float(support_params.support_density); + bool sheath = support_params.with_sheath; + bool no_sort = false; + bool done = false; + if (base_layer.layer->bottom_z < EPSILON) { + // Base flange (the 1st layer). + filler = filler_first_layer; + filler->angle = Geometry::deg2rad(float(config.support_material_angle.value + 90.)); + density = float(config.raft_first_layer_density.value * 0.01); + flow = support_params.first_layer_flow; + // use the proper spacing for first layer as we don't need to align + // its pattern to the other layers + //FIXME When paralellizing, each thread shall have its own copy of the fillers. + filler->spacing = flow.spacing(); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); + sheath = true; + no_sort = true; + } else if (config.support_material_style == SupportMaterialStyle::smsOrganic) { + tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow); + done = true; + } + if (! done) + fill_expolygons_with_sheath_generate_paths( + // Destination + base_layer.extrusions, + // Regions to fill + base_layer.polygons_to_extrude(), + // Filler and its parameters + filler, density, + // Extrusion parameters + ExtrusionRole::SupportMaterial, flow, + sheath, no_sort); + } + + // Merge base_interface_layers to base_layers to avoid unneccessary retractions + if (! base_layer.empty() && ! base_interface_layer.empty() && ! base_layer.polygons_to_extrude().empty() && ! base_interface_layer.polygons_to_extrude().empty() && + base_layer.could_merge(base_interface_layer)) + base_layer.merge(std::move(base_interface_layer)); + + layer_cache.add_nonempty_and_sort(); + + // Collect the support areas with this print_z into islands, as there is no need + // for retraction over these islands. + Polygons polys; + // Collect the extrusions, sorted by the bottom extrusion height. + for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) { + // Collect islands to polys. + layer_cache_item.layer_extruded->polygons_append(polys); + // The print_z of the top contact surfaces and bottom_z of the bottom contact surfaces are "free" + // in a sense that they are not synchronized with other support layers. As the top and bottom contact surfaces + // are inflated to achieve a better anchoring, it may happen, that these surfaces will at least partially + // overlap in Z with another support layers, leading to over-extrusion. + // Mitigate the over-extrusion by modulating the extrusion rate over these regions. + // The print head will follow the same print_z, but the layer thickness will be reduced + // where it overlaps with another support layer. + //FIXME When printing a briging path, what is an equivalent height of the squished extrudate of the same width? + // Collect overlapping top/bottom surfaces. + layer_cache_item.overlapping.reserve(20); + coordf_t bottom_z = layer_cache_item.layer_extruded->layer->bottom_print_z() + EPSILON; + auto add_overlapping = [&layer_cache_item, bottom_z](const SupportGeneratorLayersPtr &layers, size_t idx_top) { + for (int i = int(idx_top) - 1; i >= 0 && layers[i]->print_z > bottom_z; -- i) + layer_cache_item.overlapping.push_back(layers[i]); + }; + add_overlapping(top_contacts, idx_layer_top_contact); + if (layer_cache_item.layer_extruded->layer->layer_type == SupporLayerType::BottomContact) { + // Bottom contact layer may overlap with a base layer, which may be changed to interface layer. + add_overlapping(intermediate_layers, idx_layer_intermediate); + add_overlapping(interface_layers, idx_layer_interface); + add_overlapping(base_interface_layers, idx_layer_base_interface); + } + // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. + std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); + } + assert(support_layer.support_islands.empty()); + if (! polys.empty()) { + support_layer.support_islands = union_ex(polys); + support_layer.support_islands_bboxes.reserve(support_layer.support_islands.size()); + for (const ExPolygon &expoly : support_layer.support_islands) + support_layer.support_islands_bboxes.emplace_back(get_extents(expoly).inflated(SCALED_EPSILON)); + } + } // for each support_layer_id + }); + + // Now modulate the support layer height in parallel. + tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), + [&support_layers, &layer_caches] + (const tbb::blocked_range& range) { + for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { + SupportLayer &support_layer = *support_layers[support_layer_id]; + LayerCache &layer_cache = layer_caches[support_layer_id]; + // For all extrusion types at this print_z, ordered by decreasing layer height: + for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) { + // Trim the extrusion height from the bottom by the overlapping layers. + modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping); + support_layer.support_fills.append(std::move(layer_cache_item.layer_extruded->extrusions)); + } + } + }); + +#ifndef NDEBUG + struct Test { + static bool verify_nonempty(const ExtrusionEntityCollection *collection) { + for (const ExtrusionEntity *ee : collection->entities) { + if (const ExtrusionPath *path = dynamic_cast(ee)) + assert(! path->empty()); + else if (const ExtrusionMultiPath *multipath = dynamic_cast(ee)) + assert(! multipath->empty()); + else if (const ExtrusionEntityCollection *eecol = dynamic_cast(ee)) { + assert(! eecol->empty()); + return verify_nonempty(eecol); + } else + assert(false); + } + return true; + } + }; + for (const SupportLayer *support_layer : support_layers) + assert(Test::verify_nonempty(&support_layer->support_fills)); +#endif // NDEBUG +} + +/* +void PrintObjectSupportMaterial::clip_by_pillars( + const PrintObject &object, + LayersPtr &bottom_contacts, + LayersPtr &top_contacts, + LayersPtr &intermediate_contacts); + +{ + // this prevents supplying an empty point set to BoundingBox constructor + if (top_contacts.empty()) + return; + + coord_t pillar_size = scale_(PILLAR_SIZE); + coord_t pillar_spacing = scale_(PILLAR_SPACING); + + // A regular grid of pillars, filling the 2D bounding box. + Polygons grid; + { + // Rectangle with a side of 2.5x2.5mm. + Polygon pillar; + pillar.points.push_back(Point(0, 0)); + pillar.points.push_back(Point(pillar_size, 0)); + pillar.points.push_back(Point(pillar_size, pillar_size)); + pillar.points.push_back(Point(0, pillar_size)); + + // 2D bounding box of the projection of all contact polygons. + BoundingBox bbox; + for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) + bbox.merge(get_extents((*it)->polygons)); + grid.reserve(size_t(ceil(bb.size()(0) / pillar_spacing)) * size_t(ceil(bb.size()(1) / pillar_spacing))); + for (coord_t x = bb.min(0); x <= bb.max(0) - pillar_size; x += pillar_spacing) { + for (coord_t y = bb.min(1); y <= bb.max(1) - pillar_size; y += pillar_spacing) { + grid.push_back(pillar); + for (size_t i = 0; i < pillar.points.size(); ++ i) + grid.back().points[i].translate(Point(x, y)); + } + } + } + + // add pillars to every layer + for my $i (0..n_support_z) { + $shape->[$i] = [ @$grid ]; + } + + // build capitals + for my $i (0..n_support_z) { + my $z = $support_z->[$i]; + + my $capitals = intersection( + $grid, + $contact->{$z} // [], + ); + + // work on one pillar at time (if any) to prevent the capitals from being merged + // but store the contact area supported by the capital because we need to make + // sure nothing is left + my $contact_supported_by_capitals = []; + foreach my $capital (@$capitals) { + // enlarge capital tops + $capital = offset([$capital], +($pillar_spacing - $pillar_size)/2); + push @$contact_supported_by_capitals, @$capital; + + for (my $j = $i-1; $j >= 0; $j--) { + my $jz = $support_z->[$j]; + $capital = offset($capital, -$self->interface_flow->scaled_width/2); + last if !@$capitals; + push @{ $shape->[$j] }, @$capital; + } + } + + // Capitals will not generally cover the whole contact area because there will be + // remainders. For now we handle this situation by projecting such unsupported + // areas to the ground, just like we would do with a normal support. + my $contact_not_supported_by_capitals = diff( + $contact->{$z} // [], + $contact_supported_by_capitals, + ); + if (@$contact_not_supported_by_capitals) { + for (my $j = $i-1; $j >= 0; $j--) { + push @{ $shape->[$j] }, @$contact_not_supported_by_capitals; + } + } + } +} + +sub clip_with_shape { + my ($self, $support, $shape) = @_; + + foreach my $i (keys %$support) { + // don't clip bottom layer with shape so that we + // can generate a continuous base flange + // also don't clip raft layers + next if $i == 0; + next if $i < $self->object_config->raft_layers; + $support->{$i} = intersection( + $support->{$i}, + $shape->[$i], + ); + } +} +*/ + +} // namespace Slic3r diff --git a/src/libslic3r/Support/SupportCommon.hpp b/src/libslic3r/Support/SupportCommon.hpp new file mode 100644 index 0000000000..4eabce772a --- /dev/null +++ b/src/libslic3r/Support/SupportCommon.hpp @@ -0,0 +1,138 @@ +#ifndef slic3r_SupportCommon_hpp_ +#define slic3r_SupportCommon_hpp_ + +#include "../Polygon.hpp" +#include "SupportLayer.hpp" +#include "SupportParameters.hpp" + +namespace Slic3r { + +class PrintObject; +class SupportLayer; + +namespace FFFSupport { + +// Remove bridges from support contact areas. +// To be called if PrintObjectConfig::dont_support_bridges. +void remove_bridges_from_contacts( + const PrintConfig &print_config, + const Layer &lower_layer, + const LayerRegion &layerm, + float fw, + Polygons &contact_polygons); + +// Generate raft layers, also expand the 1st support layer +// in case there is no raft layer to improve support adhesion. +SupportGeneratorLayersPtr generate_raft_base( + const PrintObject &object, + const SupportParameters &support_params, + const SlicingParameters &slicing_params, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers, + const SupportGeneratorLayersPtr &base_layers, + SupportGeneratorLayerStorage &layer_storage); + +// returns sorted layers +SupportGeneratorLayersPtr generate_support_layers( + PrintObject &object, + const SupportGeneratorLayersPtr &raft_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers); + +// Produce the support G-code. +// Used by both classic and tree supports. +void generate_support_toolpaths( + SupportLayerPtrs &support_layers, + const PrintObjectConfig &config, + const SupportParameters &support_params, + const SlicingParameters &slicing_params, + const SupportGeneratorLayersPtr &raft_layers, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &intermediate_layers, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers); + +// FN_HIGHER_EQUAL: the provided object pointer has a Z value >= of an internal threshold. +// Find the first item with Z value >= of an internal threshold of fn_higher_equal. +// If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size() +// If the initial idx is size_t(-1), then use binary search. +// Otherwise search linearly upwards. +template +IndexType idx_higher_or_equal(IteratorType begin, IteratorType end, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal) +{ + auto size = int(end - begin); + if (size == 0) { + idx = 0; + } else if (idx == IndexType(-1)) { + // First of the batch of layers per thread pool invocation. Use binary search. + int idx_low = 0; + int idx_high = std::max(0, size - 1); + while (idx_low + 1 < idx_high) { + int idx_mid = (idx_low + idx_high) / 2; + if (fn_higher_equal(begin[idx_mid])) + idx_high = idx_mid; + else + idx_low = idx_mid; + } + idx = fn_higher_equal(begin[idx_low]) ? idx_low : + (fn_higher_equal(begin[idx_high]) ? idx_high : size); + } else { + // For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search. + while (int(idx) < size && ! fn_higher_equal(begin[idx])) + ++ idx; + } + return idx; +} +template +IndexType idx_higher_or_equal(const std::vector& vec, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal) +{ + return idx_higher_or_equal(vec.begin(), vec.end(), idx, fn_higher_equal); +} + +// FN_LOWER_EQUAL: the provided object pointer has a Z value <= of an internal threshold. +// Find the first item with Z value <= of an internal threshold of fn_lower_equal. +// If no vec item with Z value <= of an internal threshold of fn_lower_equal is found, return -1. +// If the initial idx is < -1, then use binary search. +// Otherwise search linearly downwards. +template +int idx_lower_or_equal(IT begin, IT end, int idx, FN_LOWER_EQUAL fn_lower_equal) +{ + auto size = int(end - begin); + if (size == 0) { + idx = -1; + } else if (idx < -1) { + // First of the batch of layers per thread pool invocation. Use binary search. + int idx_low = 0; + int idx_high = std::max(0, size - 1); + while (idx_low + 1 < idx_high) { + int idx_mid = (idx_low + idx_high) / 2; + if (fn_lower_equal(begin[idx_mid])) + idx_low = idx_mid; + else + idx_high = idx_mid; + } + idx = fn_lower_equal(begin[idx_high]) ? idx_high : + (fn_lower_equal(begin[idx_low ]) ? idx_low : -1); + } else { + // For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search. + while (idx >= 0 && ! fn_lower_equal(begin[idx])) + -- idx; + } + return idx; +} +template +int idx_lower_or_equal(const std::vector &vec, int idx, FN_LOWER_EQUAL fn_lower_equal) +{ + return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal); +} + +} // namespace FFFSupport + +} // namespace Slic3r + +#endif /* slic3r_SupportCommon_hpp_ */ diff --git a/src/libslic3r/Support/SupportDebug.cpp b/src/libslic3r/Support/SupportDebug.cpp new file mode 100644 index 0000000000..8cec806c18 --- /dev/null +++ b/src/libslic3r/Support/SupportDebug.cpp @@ -0,0 +1,108 @@ +#if 1 //#ifdef SLIC3R_DEBUG + +#include "ClipperUtils.hpp" +#include "SVG.hpp" + +#include "../Layer.hpp" +#include "SupportLayer.hpp" + +namespace Slic3r::FFFSupport { + +const char* support_surface_type_to_color_name(const SupporLayerType surface_type) +{ + switch (surface_type) { + case SupporLayerType::TopContact: return "rgb(255,0,0)"; // "red"; + case SupporLayerType::TopInterface: return "rgb(0,255,0)"; // "green"; + case SupporLayerType::Base: return "rgb(0,0,255)"; // "blue"; + case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow + case SupporLayerType::BottomContact: return "rgb(255,0,255)"; // magenta + case SupporLayerType::RaftInterface: return "rgb(0,255,255)"; + case SupporLayerType::RaftBase: return "rgb(128,128,128)"; + case SupporLayerType::Unknown: return "rgb(128,0,0)"; // maroon + default: return "rgb(64,64,64)"; + }; +} + +Point export_support_surface_type_legend_to_svg_box_size() +{ + return Point(scale_(1.+10.*8.), scale_(3.)); +} + +void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos) +{ + // 1st row + coord_t pos_x0 = pos(0) + scale_(1.); + coord_t pos_x = pos_x0; + coord_t pos_y = pos(1) + scale_(1.5); + coord_t step_x = scale_(10.); + svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(SupporLayerType::TopContact)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "top iface" , support_surface_type_to_color_name(SupporLayerType::TopInterface)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "base" , support_surface_type_to_color_name(SupporLayerType::Base)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "bottom iface" , support_surface_type_to_color_name(SupporLayerType::BottomInterface)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(SupporLayerType::BottomContact)); + // 2nd row + pos_x = pos_x0; + pos_y = pos(1)+scale_(2.8); + svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(SupporLayerType::RaftInterface)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(SupporLayerType::RaftBase)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "unknown" , support_surface_type_to_color_name(SupporLayerType::Unknown)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "intermediate" , support_surface_type_to_color_name(SupporLayerType::Intermediate)); +} + +void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, int n_layers) +{ + BoundingBox bbox; + for (int i = 0; i < n_layers; ++ i) + bbox.merge(get_extents(layers[i]->polygons)); + Point legend_size = export_support_surface_type_legend_to_svg_box_size(); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); + SVG svg(path, bbox); + const float transparency = 0.5f; + for (int i = 0; i < n_layers; ++ i) + svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency); + for (int i = 0; i < n_layers; ++ i) + svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type)); + export_support_surface_type_legend_to_svg(svg, legend_pos); + svg.Close(); +} + +void export_print_z_polygons_and_extrusions_to_svg( + const char *path, + SupportGeneratorLayer ** const layers, + int n_layers, + SupportLayer &support_layer) +{ + BoundingBox bbox; + for (int i = 0; i < n_layers; ++ i) + bbox.merge(get_extents(layers[i]->polygons)); + Point legend_size = export_support_surface_type_legend_to_svg_box_size(); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); + SVG svg(path, bbox); + const float transparency = 0.5f; + for (int i = 0; i < n_layers; ++ i) + svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency); + for (int i = 0; i < n_layers; ++ i) + svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type)); + + Polygons polygons_support, polygons_interface; + support_layer.support_fills.polygons_covered_by_width(polygons_support, float(SCALED_EPSILON)); +// support_layer.support_interface_fills.polygons_covered_by_width(polygons_interface, SCALED_EPSILON); + svg.draw(union_ex(polygons_support), "brown"); + svg.draw(union_ex(polygons_interface), "black"); + + export_support_surface_type_legend_to_svg(svg, legend_pos); + svg.Close(); +} + +} // namespace Slic3r + +#endif /* SLIC3R_DEBUG */ diff --git a/src/libslic3r/Support/SupportDebug.hpp b/src/libslic3r/Support/SupportDebug.hpp new file mode 100644 index 0000000000..22a43bc4ea --- /dev/null +++ b/src/libslic3r/Support/SupportDebug.hpp @@ -0,0 +1,18 @@ +#ifndef slic3r_SupportCommon_hpp_ +#define slic3r_SupportCommon_hpp_ + +namespace Slic3r { + +class SupportGeneratorLayer; +class SupportLayer; + +namespace FFFSupport { + +void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers); +void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer); + +} // namespace FFFSupport + +} // namespace Slic3r + +#endif /* slic3r_SupportCommon_hpp_ */ diff --git a/src/libslic3r/Support/SupportLayer.hpp b/src/libslic3r/Support/SupportLayer.hpp index 913e28136c..82265881c7 100644 --- a/src/libslic3r/Support/SupportLayer.hpp +++ b/src/libslic3r/Support/SupportLayer.hpp @@ -3,8 +3,10 @@ #include #include +// for Slic3r::deque +#include "../libslic3r.h" -namespace Slic3r { +namespace Slic3r::FFFSupport { // Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information // about the support layer type than the final support layers stored in a PrintObject. @@ -111,18 +113,30 @@ public: // Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained // up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future, // which would allocate layers by multiple chunks. -#if 0 class SupportGeneratorLayerStorage { public: + SupportGeneratorLayer& allocate_unguarded(SupporLayerType layer_type) { + m_storage.emplace_back(); + m_storage.back().layer_type = layer_type; + return m_storage.back(); + } + + SupportGeneratorLayer& allocate(SupporLayerType layer_type) + { + m_mutex.lock(); + m_storage.emplace_back(); + SupportGeneratorLayer *layer_new = &m_storage.back(); + m_mutex.unlock(); + layer_new->layer_type = layer_type; + return *layer_new; + } + private: template using Allocator = tbb::scalable_allocator; - Slic3r::deque> m_data; + Slic3r::deque> m_storage; tbb::spin_mutex m_mutex; }; -#else -#endif -using SupportGeneratorLayerStorage = std::deque; using SupportGeneratorLayersPtr = std::vector; } // namespace Slic3r diff --git a/src/libslic3r/Support/SupportParameters.cpp b/src/libslic3r/Support/SupportParameters.cpp new file mode 100644 index 0000000000..1c7f860b88 --- /dev/null +++ b/src/libslic3r/Support/SupportParameters.cpp @@ -0,0 +1,116 @@ +#include "../Print.hpp" +#include "../PrintConfig.hpp" +#include "../Slicing.hpp" +#include "SupportParameters.hpp" + +namespace Slic3r::FFFSupport { + +SupportParameters::SupportParameters(const PrintObject &object) +{ + const PrintConfig &print_config = object.print()->config(); + const PrintObjectConfig &object_config = object.config(); + const SlicingParameters &slicing_params = object.slicing_parameters(); + + this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); + this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height)); + this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height)); + this->raft_interface_flow = support_material_interface_flow; + + // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. + this->support_layer_height_min = scaled(0.01); + for (auto lh : print_config.min_layer_height.values) + this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh)); + for (auto layer : object.layers()) + this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height)); + + if (object_config.support_material_interface_layers.value == 0) { + // No interface layers allowed, print everything with the base support pattern. + this->support_material_interface_flow = this->support_material_flow; + } + + // Evaluate the XY gap between the object outer perimeters and the support structures. + // Evaluate the XY gap between the object outer perimeters and the support structures. + coordf_t external_perimeter_width = 0.; + coordf_t bridge_flow_ratio = 0; + for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = object.printing_region(region_id); + external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width())); + bridge_flow_ratio += region.config().bridge_flow_ratio; + } + this->gap_xy = object_config.support_material_xy_spacing.get_abs_value(external_perimeter_width); + bridge_flow_ratio /= object.num_printing_regions(); + + this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ? + this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : + Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter()); + + this->can_merge_support_regions = object_config.support_material_extruder.value == object_config.support_material_interface_extruder.value; + if (!this->can_merge_support_regions && (object_config.support_material_extruder.value == 0 || object_config.support_material_interface_extruder.value == 0)) { + // One of the support extruders is of "don't care" type. + auto object_extruders = object.object_extruders(); + if (object_extruders.size() == 1 && + *object_extruders.begin() == std::max(object_config.support_material_extruder.value, object_config.support_material_interface_extruder.value)) + // Object is printed with the same extruder as the support. + this->can_merge_support_regions = true; + } + + + double interface_spacing = object_config.support_material_interface_spacing.value + this->support_material_interface_flow.spacing(); + this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing); + double raft_interface_spacing = object_config.support_material_interface_spacing.value + this->raft_interface_flow.spacing(); + this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing); + double support_spacing = object_config.support_material_spacing.value + this->support_material_flow.spacing(); + this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing); + if (object_config.support_material_interface_layers.value == 0) { + // No interface layers allowed, print everything with the base support pattern. + this->interface_density = this->support_density; + } + + SupportMaterialPattern support_pattern = object_config.support_material_pattern; + this->with_sheath = object_config.support_material_with_sheath; + this->base_fill_pattern = + support_pattern == smpHoneycomb ? ipHoneycomb : + this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; + this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); + this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase; + this->contact_fill_pattern = + (object_config.support_material_interface_pattern == smipAuto && slicing_params.soluble_interface) || + object_config.support_material_interface_pattern == smipConcentric ? + ipConcentric : + (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); + + this->base_angle = Geometry::deg2rad(float(object_config.support_material_angle.value)); + this->interface_angle = Geometry::deg2rad(float(object_config.support_material_angle.value + 90.)); + this->raft_angle_1st_layer = 0.f; + this->raft_angle_base = 0.f; + this->raft_angle_interface = 0.f; + if (slicing_params.base_raft_layers > 1) { + assert(slicing_params.raft_layers() >= 4); + // There are all raft layer types (1st layer, base, interface & contact layers) available. + this->raft_angle_1st_layer = this->interface_angle; + this->raft_angle_base = this->base_angle; + this->raft_angle_interface = this->interface_angle; + if ((slicing_params.interface_raft_layers & 1) == 0) + // Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface. + this->raft_angle_interface += float(0.5 * M_PI); + } else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) { + assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3); + // 1st layer, interface & contact layers available. + this->raft_angle_1st_layer = this->base_angle; + this->raft_angle_interface = this->interface_angle + 0.5 * M_PI; + } else if (slicing_params.interface_raft_layers == 1) { + // Only the contact raft layer is non-empty, which will be printed as the 1st layer. + assert(slicing_params.base_raft_layers == 0); + assert(slicing_params.interface_raft_layers == 1); + assert(slicing_params.raft_layers() == 1); + this->raft_angle_1st_layer = float(0.5 * M_PI); + this->raft_angle_interface = this->raft_angle_1st_layer; + } else { + // No raft. + assert(slicing_params.base_raft_layers == 0); + assert(slicing_params.interface_raft_layers == 0); + assert(slicing_params.raft_layers() == 0); + } +} + +} // namespace Slic3r diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index fd4f1f8b78..904e8ffe29 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -9,6 +9,8 @@ namespace Slic3r { class PrintObject; enum InfillPattern : int; +namespace FFFSupport { + struct SupportParameters { SupportParameters(const PrintObject &object); @@ -61,6 +63,8 @@ struct SupportParameters { { return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); } }; +} // namespace FFFSupport + } // namespace Slic3r #endif /* slic3r_SupportParameters_hpp_ */ diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 224216466d..40221ec2a0 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -8,6 +8,8 @@ #include "Point.hpp" #include "MutablePolygon.hpp" +#include "Support/SupportCommon.hpp" + #include #include @@ -16,7 +18,6 @@ #include #include -#include #include #define SUPPORT_USE_AGG_RASTERIZER @@ -43,12 +44,10 @@ #include "SVG.hpp" #endif -#pragma message ("TODO: Wrap svg usages in DEBUG ifdef and remove the following include") -#include "SVG.hpp" - -// #undef NDEBUG #include +using namespace Slic3r::FFFSupport; + namespace Slic3r { // how much we extend support around the actual contact area @@ -66,103 +65,6 @@ namespace Slic3r { //#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. -#if 1 //#ifdef SLIC3R_DEBUG -const char* support_surface_type_to_color_name(const SupporLayerType surface_type) -{ - switch (surface_type) { - case SupporLayerType::TopContact: return "rgb(255,0,0)"; // "red"; - case SupporLayerType::TopInterface: return "rgb(0,255,0)"; // "green"; - case SupporLayerType::Base: return "rgb(0,0,255)"; // "blue"; - case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow - case SupporLayerType::BottomContact: return "rgb(255,0,255)"; // magenta - case SupporLayerType::RaftInterface: return "rgb(0,255,255)"; - case SupporLayerType::RaftBase: return "rgb(128,128,128)"; - case SupporLayerType::Unknown: return "rgb(128,0,0)"; // maroon - default: return "rgb(64,64,64)"; - }; -} - -Point export_support_surface_type_legend_to_svg_box_size() -{ - return Point(scale_(1.+10.*8.), scale_(3.)); -} - -void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos) -{ - // 1st row - coord_t pos_x0 = pos(0) + scale_(1.); - coord_t pos_x = pos_x0; - coord_t pos_y = pos(1) + scale_(1.5); - coord_t step_x = scale_(10.); - svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(SupporLayerType::TopContact)); - pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "top iface" , support_surface_type_to_color_name(SupporLayerType::TopInterface)); - pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "base" , support_surface_type_to_color_name(SupporLayerType::Base)); - pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "bottom iface" , support_surface_type_to_color_name(SupporLayerType::BottomInterface)); - pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(SupporLayerType::BottomContact)); - // 2nd row - pos_x = pos_x0; - pos_y = pos(1)+scale_(2.8); - svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(SupporLayerType::RaftInterface)); - pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(SupporLayerType::RaftBase)); - pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "unknown" , support_surface_type_to_color_name(SupporLayerType::Unknown)); - pos_x += step_x; - svg.draw_legend(Point(pos_x, pos_y), "intermediate" , support_surface_type_to_color_name(SupporLayerType::Intermediate)); -} - -void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, int n_layers) -{ - BoundingBox bbox; - for (int i = 0; i < n_layers; ++ i) - bbox.merge(get_extents(layers[i]->polygons)); - Point legend_size = export_support_surface_type_legend_to_svg_box_size(); - Point legend_pos(bbox.min(0), bbox.max(1)); - bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); - SVG svg(path, bbox); - const float transparency = 0.5f; - for (int i = 0; i < n_layers; ++ i) - svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency); - for (int i = 0; i < n_layers; ++ i) - svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type)); - export_support_surface_type_legend_to_svg(svg, legend_pos); - svg.Close(); -} - -void export_print_z_polygons_and_extrusions_to_svg( - const char *path, - SupportGeneratorLayer ** const layers, - int n_layers, - SupportLayer &support_layer) -{ - BoundingBox bbox; - for (int i = 0; i < n_layers; ++ i) - bbox.merge(get_extents(layers[i]->polygons)); - Point legend_size = export_support_surface_type_legend_to_svg_box_size(); - Point legend_pos(bbox.min(0), bbox.max(1)); - bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); - SVG svg(path, bbox); - const float transparency = 0.5f; - for (int i = 0; i < n_layers; ++ i) - svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency); - for (int i = 0; i < n_layers; ++ i) - svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type)); - - Polygons polygons_support, polygons_interface; - support_layer.support_fills.polygons_covered_by_width(polygons_support, float(SCALED_EPSILON)); -// support_layer.support_interface_fills.polygons_covered_by_width(polygons_interface, SCALED_EPSILON); - svg.draw(union_ex(polygons_support), "brown"); - svg.draw(union_ex(polygons_interface), "black"); - - export_support_surface_type_legend_to_svg(svg, legend_pos); - svg.Close(); -} -#endif /* SLIC3R_DEBUG */ - #ifdef SUPPORT_USE_AGG_RASTERIZER static std::vector rasterize_polygons(const Vec2i &grid_size, const double pixel_size, const Point &left_bottom, const Polygons &polygons) { @@ -326,114 +228,6 @@ static Polygons contours_simplified(const Vec2i &grid_size, const double pixel_s } #endif // SUPPORT_USE_AGG_RASTERIZER -SupportParameters::SupportParameters(const PrintObject &object) -{ - const PrintConfig &print_config = object.print()->config(); - const PrintObjectConfig &object_config = object.config(); - const SlicingParameters &slicing_params = object.slicing_parameters(); - - this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); - this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height)); - this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height)); - this->raft_interface_flow = support_material_interface_flow; - - // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. - this->support_layer_height_min = scaled(0.01); - for (auto lh : print_config.min_layer_height.values) - this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh)); - for (auto layer : object.layers()) - this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height)); - - if (object_config.support_material_interface_layers.value == 0) { - // No interface layers allowed, print everything with the base support pattern. - this->support_material_interface_flow = this->support_material_flow; - } - - // Evaluate the XY gap between the object outer perimeters and the support structures. - // Evaluate the XY gap between the object outer perimeters and the support structures. - coordf_t external_perimeter_width = 0.; - coordf_t bridge_flow_ratio = 0; - for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) { - const PrintRegion ®ion = object.printing_region(region_id); - external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width())); - bridge_flow_ratio += region.config().bridge_flow_ratio; - } - this->gap_xy = object_config.support_material_xy_spacing.get_abs_value(external_perimeter_width); - bridge_flow_ratio /= object.num_printing_regions(); - - this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ? - this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : - Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter()); - - this->can_merge_support_regions = object_config.support_material_extruder.value == object_config.support_material_interface_extruder.value; - if (!this->can_merge_support_regions && (object_config.support_material_extruder.value == 0 || object_config.support_material_interface_extruder.value == 0)) { - // One of the support extruders is of "don't care" type. - auto object_extruders = object.object_extruders(); - if (object_extruders.size() == 1 && - *object_extruders.begin() == std::max(object_config.support_material_extruder.value, object_config.support_material_interface_extruder.value)) - // Object is printed with the same extruder as the support. - this->can_merge_support_regions = true; - } - - - double interface_spacing = object_config.support_material_interface_spacing.value + this->support_material_interface_flow.spacing(); - this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing); - double raft_interface_spacing = object_config.support_material_interface_spacing.value + this->raft_interface_flow.spacing(); - this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing); - double support_spacing = object_config.support_material_spacing.value + this->support_material_flow.spacing(); - this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing); - if (object_config.support_material_interface_layers.value == 0) { - // No interface layers allowed, print everything with the base support pattern. - this->interface_density = this->support_density; - } - - SupportMaterialPattern support_pattern = object_config.support_material_pattern; - this->with_sheath = object_config.support_material_with_sheath; - this->base_fill_pattern = - support_pattern == smpHoneycomb ? ipHoneycomb : - this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; - this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); - this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase; - this->contact_fill_pattern = - (object_config.support_material_interface_pattern == smipAuto && slicing_params.soluble_interface) || - object_config.support_material_interface_pattern == smipConcentric ? - ipConcentric : - (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); - - this->base_angle = Geometry::deg2rad(float(object_config.support_material_angle.value)); - this->interface_angle = Geometry::deg2rad(float(object_config.support_material_angle.value + 90.)); - this->raft_angle_1st_layer = 0.f; - this->raft_angle_base = 0.f; - this->raft_angle_interface = 0.f; - if (slicing_params.base_raft_layers > 1) { - assert(slicing_params.raft_layers() >= 4); - // There are all raft layer types (1st layer, base, interface & contact layers) available. - this->raft_angle_1st_layer = this->interface_angle; - this->raft_angle_base = this->base_angle; - this->raft_angle_interface = this->interface_angle; - if ((slicing_params.interface_raft_layers & 1) == 0) - // Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface. - this->raft_angle_interface += float(0.5 * M_PI); - } else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) { - assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3); - // 1st layer, interface & contact layers available. - this->raft_angle_1st_layer = this->base_angle; - this->raft_angle_interface = this->interface_angle + 0.5 * M_PI; - } else if (slicing_params.interface_raft_layers == 1) { - // Only the contact raft layer is non-empty, which will be printed as the 1st layer. - assert(slicing_params.base_raft_layers == 0); - assert(slicing_params.interface_raft_layers == 1); - assert(slicing_params.raft_layers() == 1); - this->raft_angle_1st_layer = float(0.5 * M_PI); - this->raft_angle_interface = this->raft_angle_1st_layer; - } else { - // No raft. - assert(slicing_params.base_raft_layers == 0); - assert(slicing_params.interface_raft_layers == 0); - assert(slicing_params.raft_layers() == 0); - } -} - PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : m_print_config (&object->print()->config()), m_object_config (&object->config()), @@ -442,39 +236,6 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object { } -// Using the std::deque as an allocator. -inline SupportGeneratorLayer& layer_allocate( - std::deque &layer_storage, - SupporLayerType layer_type) -{ - layer_storage.push_back(SupportGeneratorLayer()); - layer_storage.back().layer_type = layer_type; - return layer_storage.back(); -} - -inline SupportGeneratorLayer& layer_allocate( - std::deque &layer_storage, - tbb::spin_mutex &layer_storage_mutex, - SupporLayerType layer_type) -{ - layer_storage_mutex.lock(); - layer_storage.push_back(SupportGeneratorLayer()); - SupportGeneratorLayer *layer_new = &layer_storage.back(); - layer_storage_mutex.unlock(); - layer_new->layer_type = layer_type; - return *layer_new; -} - -inline void layers_append(SupportGeneratorLayersPtr &dst, const SupportGeneratorLayersPtr &src) -{ - dst.insert(dst.end(), src.begin(), src.end()); -} - -// Support layer that is covered by some form of dense interface. -static constexpr const std::initializer_list support_types_interface { - SupporLayerType::RaftInterface, SupporLayerType::BottomContact, SupporLayerType::BottomInterface, SupporLayerType::TopContact, SupporLayerType::TopInterface -}; - void PrintObjectSupportMaterial::generate(PrintObject &object) { BOOST_LOG_TRIVIAL(info) << "Support generator - Start"; @@ -1296,86 +1057,6 @@ namespace SupportMaterialInternal { } } -void remove_bridges_from_contacts( - const PrintConfig &print_config, - const Layer &lower_layer, - const LayerRegion &layerm, - float fw, - Polygons &contact_polygons) -{ - // compute the area of bridging perimeters - Polygons bridges; - { - // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported. - Polygons lower_grown_slices = expand(lower_layer.lslices, - //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width. - 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm.region().config().perimeter_extruder-1))), - SUPPORT_SURFACES_OFFSET_PARAMETERS); - // Collect perimeters of this layer. - //FIXME split_at_first_point() could split a bridge mid-way - #if 0 - Polylines overhang_perimeters = layerm.perimeters.as_polylines(); - // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() - for (Polyline &polyline : overhang_perimeters) - polyline.points[0].x += 1; - // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters. - overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); - #else - Polylines overhang_perimeters = diff_pl(layerm.perimeters().as_polylines(), lower_grown_slices); - #endif - - // only consider straight overhangs - // only consider overhangs having endpoints inside layer's slices - // convert bridging polylines into polygons by inflating them with their thickness - // since we're dealing with bridges, we can't assume width is larger than spacing, - // so we take the largest value and also apply safety offset to be ensure no gaps - // are left in between - Flow perimeter_bridge_flow = layerm.bridging_flow(frPerimeter); - //FIXME one may want to use a maximum of bridging flow width and normal flow width, as the perimeters are calculated using the normal flow - // and then turned to bridging flow, thus their centerlines are derived from non-bridging flow and expanding them by a bridging flow - // may not expand them to the edge of their respective islands. - const float w = float(0.5 * std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())) + scaled(0.001); - for (Polyline &polyline : overhang_perimeters) - if (polyline.is_straight()) { - // This is a bridge - polyline.extend_start(fw); - polyline.extend_end(fw); - // Is the straight perimeter segment supported at both sides? - Point pts[2] = { polyline.first_point(), polyline.last_point() }; - bool supported[2] = { false, false }; - for (size_t i = 0; i < lower_layer.lslices.size() && ! (supported[0] && supported[1]); ++ i) - for (int j = 0; j < 2; ++ j) - if (! supported[j] && lower_layer.lslices_ex[i].bbox.contains(pts[j]) && lower_layer.lslices[i].contains(pts[j])) - supported[j] = true; - if (supported[0] && supported[1]) - // Offset a polyline into a thick line. - polygons_append(bridges, offset(polyline, w)); - } - bridges = union_(bridges); - } - // remove the entire bridges and only support the unsupported edges - //FIXME the brided regions are already collected as layerm.bridged. Use it? - for (const Surface &surface : layerm.fill_surfaces()) - if (surface.surface_type == stBottomBridge && surface.bridge_angle >= 0.0) - polygons_append(bridges, surface.expolygon); - //FIXME add the gap filled areas. Extrude the gaps with a bridge flow? - // Remove the unsupported ends of the bridges from the bridged areas. - //FIXME add supports at regular intervals to support long bridges! - bridges = diff(bridges, - // Offset unsupported edges into polygons. - offset(layerm.unsupported_bridge_edges(), scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); - // Remove bridged areas from the supported areas. - contact_polygons = diff(contact_polygons, bridges, ApplySafetyOffset::Yes); - - #ifdef SLIC3R_DEBUG - static int iRun = 0; - SVG::export_expolygons(debug_out_path("support-top-contacts-remove-bridges-run%d.svg", iRun ++), - { { { union_ex(offset(layerm.unsupported_bridge_edges(), scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)) }, { "unsupported_bridge_edges", "orange", 0.5f } }, - { { union_ex(contact_polygons) }, { "contact_polygons", "blue", 0.5f } }, - { { union_ex(bridges) }, { "bridges", "red", "black", "", scaled(0.1f), 0.5f } } }); - #endif /* SLIC3R_DEBUG */ -} - std::vector PrintObjectSupportMaterial::buildplate_covered(const PrintObject &object) const { // Build support on a build plate only? If so, then collect and union all the surfaces below the current layer. @@ -1674,8 +1355,7 @@ static inline std::pair new_cont const SlicingParameters &slicing_params, const coordf_t support_layer_height_min, const Layer &layer, - std::deque &layer_storage, - tbb::spin_mutex &layer_storage_mutex) + SupportGeneratorLayerStorage &layer_storage) { double print_z, bottom_z, height; SupportGeneratorLayer* bridging_layer = nullptr; @@ -1735,7 +1415,7 @@ static inline std::pair new_cont } if (bridging_print_z < print_z - EPSILON) { // Allocate the new layer. - bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::TopContact); + bridging_layer = &layer_storage.allocate(SupporLayerType::TopContact); bridging_layer->idx_object_layer_above = layer_id; bridging_layer->print_z = bridging_print_z; if (bridging_print_z == slicing_params.first_print_layer_height) { @@ -1751,7 +1431,7 @@ static inline std::pair new_cont } } - SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::TopContact); + SupportGeneratorLayer &new_layer = layer_storage.allocate(SupporLayerType::TopContact); new_layer.idx_object_layer_above = layer_id; new_layer.print_z = print_z; new_layer.bottom_z = bottom_z; @@ -1983,9 +1663,8 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow, // and the other for the overhangs extruded with a normal flow. contact_out.assign(num_layers * 2, nullptr); - tbb::spin_mutex layer_storage_mutex; tbb::parallel_for(tbb::blocked_range(this->has_raft() ? 0 : 1, num_layers), - [this, &object, &annotations, &layer_storage, &layer_storage_mutex, &contact_out] + [this, &object, &annotations, &layer_storage, &contact_out] (const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { @@ -2003,7 +1682,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( // Now apply the contact areas to the layer where they need to be made. if (! contact_polygons.empty() || ! overhang_polygons.empty()) { // Allocate the two empty layers. - auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, m_support_params.support_layer_height_min, layer, layer_storage, layer_storage_mutex); + auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, m_support_params.support_layer_height_min, layer, layer_storage); if (new_layer) { // Fill the non-bridging layer with polygons. fill_contact_layer(*new_layer, layer_id, m_slicing_params, @@ -2053,7 +1732,7 @@ static inline SupportGeneratorLayer* detect_bottom_contacts( // First top contact layer index overlapping with this new bottom interface layer. size_t contact_idx, // To allocate a new layer from. - std::deque &layer_storage, + SupportGeneratorLayerStorage &layer_storage, // To trim the support areas above this bottom interface layer with this newly created bottom interface layer. std::vector &layer_support_areas, // Support areas projected from top to bottom, starting with top support interfaces. @@ -2088,7 +1767,7 @@ static inline SupportGeneratorLayer* detect_bottom_contacts( size_t layer_id = layer.id() - slicing_params.raft_layers(); // Allocate a new bottom contact layer. - SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::BottomContact); + SupportGeneratorLayer &layer_new = layer_storage.allocate_unguarded(SupporLayerType::BottomContact); // Grow top surfaces so that interface and support generation are generated // with some spacing from object - it looks we don't need the actual // top shapes so this can be done here @@ -2392,80 +2071,6 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_ return bottom_contacts; } -// FN_HIGHER_EQUAL: the provided object pointer has a Z value >= of an internal threshold. -// Find the first item with Z value >= of an internal threshold of fn_higher_equal. -// If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size() -// If the initial idx is size_t(-1), then use binary search. -// Otherwise search linearly upwards. -template -IndexType idx_higher_or_equal(IteratorType begin, IteratorType end, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal) -{ - auto size = int(end - begin); - if (size == 0) { - idx = 0; - } else if (idx == IndexType(-1)) { - // First of the batch of layers per thread pool invocation. Use binary search. - int idx_low = 0; - int idx_high = std::max(0, size - 1); - while (idx_low + 1 < idx_high) { - int idx_mid = (idx_low + idx_high) / 2; - if (fn_higher_equal(begin[idx_mid])) - idx_high = idx_mid; - else - idx_low = idx_mid; - } - idx = fn_higher_equal(begin[idx_low]) ? idx_low : - (fn_higher_equal(begin[idx_high]) ? idx_high : size); - } else { - // For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search. - while (int(idx) < size && ! fn_higher_equal(begin[idx])) - ++ idx; - } - return idx; -} -template -IndexType idx_higher_or_equal(const std::vector& vec, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal) -{ - return idx_higher_or_equal(vec.begin(), vec.end(), idx, fn_higher_equal); -} - -// FN_LOWER_EQUAL: the provided object pointer has a Z value <= of an internal threshold. -// Find the first item with Z value <= of an internal threshold of fn_lower_equal. -// If no vec item with Z value <= of an internal threshold of fn_lower_equal is found, return -1. -// If the initial idx is < -1, then use binary search. -// Otherwise search linearly downwards. -template -int idx_lower_or_equal(IT begin, IT end, int idx, FN_LOWER_EQUAL fn_lower_equal) -{ - auto size = int(end - begin); - if (size == 0) { - idx = -1; - } else if (idx < -1) { - // First of the batch of layers per thread pool invocation. Use binary search. - int idx_low = 0; - int idx_high = std::max(0, size - 1); - while (idx_low + 1 < idx_high) { - int idx_mid = (idx_low + idx_high) / 2; - if (fn_lower_equal(begin[idx_mid])) - idx_low = idx_mid; - else - idx_high = idx_mid; - } - idx = fn_lower_equal(begin[idx_high]) ? idx_high : - (fn_lower_equal(begin[idx_low ]) ? idx_low : -1); - } else { - // For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search. - while (idx >= 0 && ! fn_lower_equal(begin[idx])) - -- idx; - } - return idx; -} -template -int idx_lower_or_equal(const std::vector &vec, int idx, FN_LOWER_EQUAL fn_lower_equal) -{ - return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal); -} - // Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them. void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts( const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const @@ -2561,7 +2166,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp assert(extr2->bottom_z == m_slicing_params.first_print_layer_height); assert(extr2->print_z >= m_slicing_params.first_print_layer_height + m_support_params.support_layer_height_min - EPSILON); if (intermediate_layers.empty() || intermediate_layers.back()->print_z < m_slicing_params.first_print_layer_height) { - SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); + SupportGeneratorLayer &layer_new = layer_storage.allocate_unguarded(SupporLayerType::Intermediate); layer_new.bottom_z = 0.; layer_new.print_z = m_slicing_params.first_print_layer_height; layer_new.height = m_slicing_params.first_print_layer_height; @@ -2583,7 +2188,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp // At this point only layers above first_print_layer_heigth + EPSILON are expected as the other cases were captured earlier. assert(extr2z >= m_slicing_params.first_print_layer_height + EPSILON); // Generate a new intermediate layer. - SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); + SupportGeneratorLayer &layer_new = layer_storage.allocate_unguarded(SupporLayerType::Intermediate); layer_new.bottom_z = 0.; layer_new.print_z = extr1z = m_slicing_params.first_print_layer_height; layer_new.height = extr1z; @@ -2603,7 +2208,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp ++ idx_layer_object; if (idx_layer_object == 0 && extr1z == m_slicing_params.raft_interface_top_z) { // Insert one base support layer below the object. - SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); + SupportGeneratorLayer &layer_new = layer_storage.allocate_unguarded(SupporLayerType::Intermediate); layer_new.print_z = m_slicing_params.object_print_z_min; layer_new.bottom_z = m_slicing_params.raft_interface_top_z; layer_new.height = layer_new.print_z - layer_new.bottom_z; @@ -2611,7 +2216,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp } // Emit all intermediate support layers synchronized with object layers up to extr2z. for (; idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) { - SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); + SupportGeneratorLayer &layer_new = layer_storage.allocate_unguarded(SupporLayerType::Intermediate); layer_new.print_z = object.layers()[idx_layer_object]->print_z; layer_new.height = object.layers()[idx_layer_object]->height; layer_new.bottom_z = (idx_layer_object > 0) ? object.layers()[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height); @@ -2629,7 +2234,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp // between the 1st intermediate layer print_z and extr1->print_z is not too small. assert(extr1->bottom_z + m_support_params.support_layer_height_min < extr1->print_z + EPSILON); // Generate the first intermediate layer. - SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); + SupportGeneratorLayer &layer_new = layer_storage.allocate_unguarded(SupporLayerType::Intermediate); layer_new.bottom_z = extr1->bottom_z; layer_new.print_z = extr1z = extr1->print_z; layer_new.height = extr1->height; @@ -2653,7 +2258,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp coordf_t extr2z_large_steps = extr2z; // Take the largest allowed step in the Z axis until extr2z_large_steps is reached. for (size_t i = 0; i < n_layers_extra; ++ i) { - SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::Intermediate); + SupportGeneratorLayer &layer_new = layer_storage.allocate_unguarded(SupporLayerType::Intermediate); if (i + 1 == n_layers_extra) { // Last intermediate layer added. Align the last entered layer with extr2z_large_steps exactly. layer_new.bottom_z = (i == 0) ? extr1z : intermediate_layers.back()->print_z; @@ -2909,163 +2514,6 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end"; } -SupportGeneratorLayersPtr generate_raft_base( - const PrintObject &object, - const SupportParameters &support_params, - const SlicingParameters &slicing_params, - const SupportGeneratorLayersPtr &top_contacts, - const SupportGeneratorLayersPtr &interface_layers, - const SupportGeneratorLayersPtr &base_interface_layers, - const SupportGeneratorLayersPtr &base_layers, - SupportGeneratorLayerStorage &layer_storage) -{ - // If there is brim to be generated, calculate the trimming regions. - Polygons brim; - if (object.has_brim()) { - // The object does not have a raft. - // Calculate the area covered by the brim. - const BrimType brim_type = object.config().brim_type; - const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner; - const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner; - const auto brim_separation = scaled(object.config().brim_separation.value + object.config().brim_width.value); - for (const ExPolygon &ex : object.layers().front()->lslices) { - if (brim_outer && brim_inner) - polygons_append(brim, offset(ex, brim_separation)); - else { - if (brim_outer) - polygons_append(brim, offset(ex.contour, brim_separation, ClipperLib::jtRound, float(scale_(0.1)))); - else - brim.emplace_back(ex.contour); - if (brim_inner) { - Polygons holes = ex.holes; - polygons_reverse(holes); - holes = shrink(holes, brim_separation, ClipperLib::jtRound, float(scale_(0.1))); - polygons_reverse(holes); - polygons_append(brim, std::move(holes)); - } else - polygons_append(brim, ex.holes); - } - } - brim = union_(brim); - } - - // How much to inflate the support columns to be stable. This also applies to the 1st layer, if no raft layers are to be printed. - const float inflate_factor_fine = float(scale_((slicing_params.raft_layers() > 1) ? 0.5 : EPSILON)); - const float inflate_factor_1st_layer = std::max(0.f, float(scale_(object.config().raft_first_layer_expansion)) - inflate_factor_fine); - SupportGeneratorLayer *contacts = top_contacts .empty() ? nullptr : top_contacts .front(); - SupportGeneratorLayer *interfaces = interface_layers .empty() ? nullptr : interface_layers .front(); - SupportGeneratorLayer *base_interfaces = base_interface_layers.empty() ? nullptr : base_interface_layers.front(); - SupportGeneratorLayer *columns_base = base_layers .empty() ? nullptr : base_layers .front(); - if (contacts != nullptr && contacts->print_z > std::max(slicing_params.first_print_layer_height, slicing_params.raft_contact_top_z) + EPSILON) - // This is not the raft contact layer. - contacts = nullptr; - if (interfaces != nullptr && interfaces->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) - // This is not the raft column base layer. - interfaces = nullptr; - if (base_interfaces != nullptr && base_interfaces->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) - // This is not the raft column base layer. - base_interfaces = nullptr; - if (columns_base != nullptr && columns_base->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) - // This is not the raft interface layer. - columns_base = nullptr; - - Polygons interface_polygons; - if (contacts != nullptr && ! contacts->polygons.empty()) - polygons_append(interface_polygons, expand(contacts->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - if (interfaces != nullptr && ! interfaces->polygons.empty()) - polygons_append(interface_polygons, expand(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - if (base_interfaces != nullptr && ! base_interfaces->polygons.empty()) - polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - - // Output vector. - SupportGeneratorLayersPtr raft_layers; - - if (slicing_params.raft_layers() > 1) { - Polygons base; - Polygons columns; - Polygons first_layer; - if (columns_base != nullptr) { - if (columns_base->bottom_print_z() > slicing_params.raft_interface_top_z - EPSILON) { - // Classic supports with colums above the raft interface. - base = columns_base->polygons; - columns = base; - if (! interface_polygons.empty()) - // Trim the 1st layer columns with the inflated interface polygons. - columns = diff(columns, interface_polygons); - } else { - // Organic supports with raft on print bed. - assert(is_approx(columns_base->print_z, slicing_params.first_print_layer_height)); - first_layer = columns_base->polygons; - } - } - if (! interface_polygons.empty()) { - // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. - base = union_(base, interface_polygons); - } - // Do not add the raft contact layer, only add the raft layers below the contact layer. - // Insert the 1st layer. - { - SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, (slicing_params.base_raft_layers > 0) ? SupporLayerType::RaftBase : SupporLayerType::RaftInterface); - raft_layers.push_back(&new_layer); - new_layer.print_z = slicing_params.first_print_layer_height; - new_layer.height = slicing_params.first_print_layer_height; - new_layer.bottom_z = 0.; - first_layer = union_(std::move(first_layer), base); - new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(first_layer, inflate_factor_1st_layer) : first_layer; - } - // Insert the base layers. - for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { - coordf_t print_z = raft_layers.back()->print_z; - SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, SupporLayerType::RaftBase); - raft_layers.push_back(&new_layer); - new_layer.print_z = print_z + slicing_params.base_raft_layer_height; - new_layer.height = slicing_params.base_raft_layer_height; - new_layer.bottom_z = print_z; - new_layer.polygons = base; - } - // Insert the interface layers. - for (size_t i = 1; i < slicing_params.interface_raft_layers; ++ i) { - coordf_t print_z = raft_layers.back()->print_z; - SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, SupporLayerType::RaftInterface); - raft_layers.push_back(&new_layer); - new_layer.print_z = print_z + slicing_params.interface_raft_layer_height; - new_layer.height = slicing_params.interface_raft_layer_height; - new_layer.bottom_z = print_z; - new_layer.polygons = interface_polygons; - //FIXME misusing contact_polygons for support columns. - new_layer.contact_polygons = std::make_unique(columns); - } - } else { - if (columns_base != nullptr) { - // Expand the bases of the support columns in the 1st layer. - Polygons &raft = columns_base->polygons; - Polygons trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); - if (inflate_factor_1st_layer > SCALED_EPSILON) { - // Inflate in multiple steps to avoid leaking of the support 1st layer through object walls. - auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / support_params.first_layer_flow.scaled_width()))); - float step = inflate_factor_1st_layer / nsteps; - for (int i = 0; i < nsteps; ++ i) - raft = diff(expand(raft, step), trimming); - } else - raft = diff(raft, trimming); - if (! interface_polygons.empty()) - columns_base->polygons = diff(columns_base->polygons, interface_polygons); - } - if (! brim.empty()) { - if (columns_base) - columns_base->polygons = diff(columns_base->polygons, brim); - if (contacts) - contacts->polygons = diff(contacts->polygons, brim); - if (interfaces) - interfaces->polygons = diff(interfaces->polygons, brim); - if (base_interfaces) - base_interfaces->polygons = diff(base_interfaces->polygons, brim); - } - } - - return raft_layers; -} - // Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers. std::pair PrintObjectSupportMaterial::generate_interface_layers( const SupportGeneratorLayersPtr &bottom_contacts, @@ -3111,9 +2559,8 @@ std::pair PrintObjectSuppo auto smoothing_distance = m_support_params.support_material_interface_flow.scaled_spacing() * 1.5; auto minimum_island_radius = m_support_params.support_material_interface_flow.scaled_spacing() / m_support_params.interface_density; auto closing_distance = smoothing_distance; // scaled(m_object_config->support_material_closing_radius.value); - tbb::spin_mutex layer_storage_mutex; // Insert a new layer into base_interface_layers, if intersection with base exists. - auto insert_layer = [&layer_storage, &layer_storage_mutex, snug_supports, closing_distance, smoothing_distance, minimum_island_radius]( + auto insert_layer = [&layer_storage, snug_supports, closing_distance, smoothing_distance, minimum_island_radius]( SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* { assert(! bottom.empty() || ! top.empty()); // Merge top into bottom, unite them with a safety offset. @@ -3128,7 +2575,7 @@ std::pair PrintObjectSuppo //FIXME Remove non-printable tiny islands, let them be printed using the base support. //bottom = opening(std::move(bottom), minimum_island_radius); if (! bottom.empty()) { - SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, layer_storage_mutex, type); + SupportGeneratorLayer &layer_new = layer_storage.allocate(type); layer_new.polygons = std::move(bottom); layer_new.print_z = intermediate_layer.print_z; layer_new.bottom_z = intermediate_layer.bottom_z; @@ -3230,1428 +2677,6 @@ std::pair PrintObjectSuppo return base_and_interface_layers; } -static inline void fill_expolygon_generate_paths( - ExtrusionEntitiesPtr &dst, - ExPolygon &&expolygon, - Fill *filler, - const FillParams &fill_params, - float density, - ExtrusionRole role, - const Flow &flow) -{ - Surface surface(stInternal, std::move(expolygon)); - Polylines polylines; - try { - assert(!fill_params.use_arachne); - polylines = filler->fill_surface(&surface, fill_params); - } catch (InfillFailedException &) { - } - extrusion_entities_append_paths( - dst, - std::move(polylines), - role, - flow.mm3_per_mm(), flow.width(), flow.height()); -} - -static inline void fill_expolygons_generate_paths( - ExtrusionEntitiesPtr &dst, - ExPolygons &&expolygons, - Fill *filler, - const FillParams &fill_params, - float density, - ExtrusionRole role, - const Flow &flow) -{ - for (ExPolygon &expoly : expolygons) - fill_expolygon_generate_paths(dst, std::move(expoly), filler, fill_params, density, role, flow); -} - -static inline void fill_expolygons_generate_paths( - ExtrusionEntitiesPtr &dst, - ExPolygons &&expolygons, - Fill *filler, - float density, - ExtrusionRole role, - const Flow &flow) -{ - FillParams fill_params; - fill_params.density = density; - fill_params.dont_adjust = true; - fill_expolygons_generate_paths(dst, std::move(expolygons), filler, fill_params, density, role, flow); -} - -static Polylines draw_perimeters(const ExPolygon &expoly, double clip_length) -{ - // Draw the perimeters. - Polylines polylines; - polylines.reserve(expoly.holes.size() + 1); - for (size_t i = 0; i <= expoly.holes.size(); ++ i) { - Polyline pl(i == 0 ? expoly.contour.points : expoly.holes[i - 1].points); - pl.points.emplace_back(pl.points.front()); - if (i > 0) - // It is a hole, reverse it. - pl.reverse(); - // so that all contours are CCW oriented. - pl.clip_end(clip_length); - polylines.emplace_back(std::move(pl)); - } - return polylines; -} - -static inline void tree_supports_generate_paths( - ExtrusionEntitiesPtr &dst, - const Polygons &polygons, - const Flow &flow) -{ - // Offset expolygon inside, returns number of expolygons collected (0 or 1). - // Vertices of output paths are marked with Z = source contour index of the expoly. - // Vertices at the intersection of source contours are marked with Z = -1. - auto shrink_expolygon_with_contour_idx = [](const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib_Z::Paths &out) -> int - { - assert(delta > 0); - auto append_paths_with_z = [](ClipperLib::Paths &src, coord_t contour_idx, ClipperLib_Z::Paths &dst) { - dst.reserve(next_highest_power_of_2(dst.size() + src.size())); - for (const ClipperLib::Path &contour : src) { - ClipperLib_Z::Path tmp; - tmp.reserve(contour.size()); - for (const Point &p : contour) - tmp.emplace_back(p.x(), p.y(), contour_idx); - dst.emplace_back(std::move(tmp)); - } - }; - - // 1) Offset the outer contour. - ClipperLib_Z::Paths contours; - { - ClipperLib::ClipperOffset co; - if (joinType == jtRound) - co.ArcTolerance = miterLimit; - else - co.MiterLimit = miterLimit; - co.ShortestEdgeLength = double(delta * 0.005); - co.AddPath(expoly.contour.points, joinType, ClipperLib::etClosedPolygon); - ClipperLib::Paths contours_raw; - co.Execute(contours_raw, - delta); - if (contours_raw.empty()) - // No need to try to offset the holes. - return 0; - append_paths_with_z(contours_raw, 0, contours); - } - - if (expoly.holes.empty()) { - // No need to subtract holes from the offsetted expolygon, we are done. - append(out, std::move(contours)); - } else { - // 2) Offset the holes one by one, collect the offsetted holes. - ClipperLib_Z::Paths holes; - { - for (const Polygon &hole : expoly.holes) { - ClipperLib::ClipperOffset co; - if (joinType == jtRound) - co.ArcTolerance = miterLimit; - else - co.MiterLimit = miterLimit; - co.ShortestEdgeLength = double(delta * 0.005); - co.AddPath(hole.points, joinType, ClipperLib::etClosedPolygon); - ClipperLib::Paths out2; - // Execute reorients the contours so that the outer most contour has a positive area. Thus the output - // contours will be CCW oriented even though the input paths are CW oriented. - // Offset is applied after contour reorientation, thus the signum of the offset value is reversed. - co.Execute(out2, delta); - append_paths_with_z(out2, 1 + (&hole - expoly.holes.data()), holes); - } - } - - // 3) Subtract holes from the contours. - if (holes.empty()) { - // No hole remaining after an offset. Just copy the outer contour. - append(out, std::move(contours)); - } else { - // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. - // Subtract the offsetted holes from the offsetted contours. - 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) { - //pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z())); - // Just mark the intersection. - pt.z() = -1; - }); - clipper.AddPaths(contours, ClipperLib_Z::ptSubject, true); - clipper.AddPaths(holes, ClipperLib_Z::ptClip, true); - ClipperLib_Z::Paths output; - clipper.Execute(ClipperLib_Z::ctDifference, output, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero); - if (! output.empty()) { - append(out, std::move(output)); - } else { - // The offsetted holes have eaten up the offsetted outer contour. - return 0; - } - } - } - - return 1; - }; - - const double spacing = flow.scaled_spacing(); - // Clip the sheath path to avoid the extruder to get exactly on the first point of the loop. - const double clip_length = spacing * 0.15; - const double anchor_length = spacing * 6.; - ClipperLib_Z::Paths anchor_candidates; - for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) { - std::unique_ptr eec; - double area = expoly.area(); - if (area > sqr(scaled(5.))) { - eec = std::make_unique(); - // Don't reoder internal / external loops of the same island, always start with the internal loop. - eec->no_sort = true; - // Make the tree branch stable by adding another perimeter. - ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); - if (level2.size() == 1) { - Polylines polylines; - extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), - // Disable reversal of the path, always start with the anchor, always print CCW. - false); - expoly = level2.front(); - } - } - - // Try to produce one more perimeter to place the seam anchor. - // First genrate a 2nd perimeter loop as a source for anchor candidates. - // The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection. - anchor_candidates.clear(); - shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates); - // Orient all contours CW. - for (auto &path : anchor_candidates) - if (ClipperLib_Z::Area(path) > 0) - std::reverse(path.begin(), path.end()); - - // Draw the perimeters. - Polylines polylines; - polylines.reserve(expoly.holes.size() + 1); - for (size_t idx_loop = 0; idx_loop < expoly.num_contours(); ++ idx_loop) { - // Open the loop with a seam. - const Polygon &loop = expoly.contour_or_hole(idx_loop); - Polyline pl(loop.points); - // Orient all contours CW, because the anchor will be added to the end of polyline while we want to start a loop with the anchor. - if (idx_loop == 0) - // It is an outer contour. - pl.reverse(); - pl.points.emplace_back(pl.points.front()); - pl.clip_end(clip_length); - if (pl.size() < 2) - continue; - // Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour. - ClipperLib_Z::Path *closest_contour = nullptr; - Vec2d closest_point; - int closest_point_idx = -1; - double closest_point_t; - double d2min = std::numeric_limits::max(); - Vec2d seam_pt = pl.back().cast(); - for (ClipperLib_Z::Path &path : anchor_candidates) - for (int i = 0; i < path.size(); ++ i) { - int j = next_idx_modulo(i, path); - if (path[i].z() == idx_loop || path[j].z() == idx_loop) { - Vec2d pi(path[i].x(), path[i].y()); - Vec2d pj(path[j].x(), path[j].y()); - Vec2d v = pj - pi; - Vec2d w = seam_pt - pi; - auto l2 = v.squaredNorm(); - auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.); - if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) { - // Closest point. - Vec2d fp = pi + v * t; - double d2 = (fp - seam_pt).squaredNorm(); - if (d2 < d2min) { - d2min = d2; - closest_contour = &path; - closest_point = fp; - closest_point_idx = i; - closest_point_t = t; - } - } - } - } - if (d2min < sqr(flow.scaled_width() * 3.)) { - // Try to cut an anchor from the closest_contour. - // Both closest_contour and pl are CW oriented. - pl.points.emplace_back(closest_point.cast()); - const ClipperLib_Z::Path &path = *closest_contour; - double remaining_length = anchor_length - (seam_pt - closest_point).norm(); - int i = closest_point_idx; - int j = next_idx_modulo(i, *closest_contour); - Vec2d pi(path[i].x(), path[i].y()); - Vec2d pj(path[j].x(), path[j].y()); - Vec2d v = pj - pi; - double l = v.norm(); - if (remaining_length < (1. - closest_point_t) * l) { - // Just trim the current line. - pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast()); - } else { - // Take the rest of the current line, continue with the other lines. - pl.points.emplace_back(path[j].x(), path[j].y()); - pi = pj; - for (i = j; path[i].z() == idx_loop && remaining_length > 0; i = j, pi = pj) { - j = next_idx_modulo(i, path); - pj = Vec2d(path[j].x(), path[j].y()); - v = pj - pi; - l = v.norm(); - if (i == closest_point_idx) { - // Back at the first segment. Most likely this should not happen and we may end the anchor. - break; - } - if (remaining_length <= l) { - pl.points.emplace_back((pi + v * (remaining_length / l)).cast()); - break; - } - pl.points.emplace_back(path[j].x(), path[j].y()); - remaining_length -= l; - } - } - } - // Start with the anchor. - pl.reverse(); - polylines.emplace_back(std::move(pl)); - } - - ExtrusionEntitiesPtr &out = eec ? eec->entities : dst; - extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), - // Disable reversal of the path, always start with the anchor, always print CCW. - false); - if (eec) { - std::reverse(eec->entities.begin(), eec->entities.end()); - dst.emplace_back(eec.release()); - } - } -} - -static inline void fill_expolygons_with_sheath_generate_paths( - ExtrusionEntitiesPtr &dst, - const Polygons &polygons, - Fill *filler, - float density, - ExtrusionRole role, - const Flow &flow, - bool with_sheath, - bool no_sort) -{ - if (polygons.empty()) - return; - - if (! with_sheath) { - fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow); - return; - } - - FillParams fill_params; - fill_params.density = density; - fill_params.dont_adjust = true; - - const double spacing = flow.scaled_spacing(); - // Clip the sheath path to avoid the extruder to get exactly on the first point of the loop. - const double clip_length = spacing * 0.15; - - for (ExPolygon &expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5*flow.scaled_width()))) { - // Don't reorder the skirt and its infills. - std::unique_ptr eec; - if (no_sort) { - eec = std::make_unique(); - eec->no_sort = true; - } - ExtrusionEntitiesPtr &out = no_sort ? eec->entities : dst; - extrusion_entities_append_paths(out, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); - // Fill in the rest. - fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); - if (no_sort && ! eec->empty()) - dst.emplace_back(eec.release()); - } -} - -// Support layers, partially processed. -struct SupportGeneratorLayerExtruded -{ - SupportGeneratorLayerExtruded& operator=(SupportGeneratorLayerExtruded &&rhs) { - this->layer = rhs.layer; - this->extrusions = std::move(rhs.extrusions); - m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude); - rhs.layer = nullptr; - return *this; - } - - bool empty() const { - return layer == nullptr || layer->polygons.empty(); - } - - void set_polygons_to_extrude(Polygons &&polygons) { - if (m_polygons_to_extrude == nullptr) - m_polygons_to_extrude = std::make_unique(std::move(polygons)); - else - *m_polygons_to_extrude = std::move(polygons); - } - Polygons& polygons_to_extrude() { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } - const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } - - bool could_merge(const SupportGeneratorLayerExtruded &other) const { - return ! this->empty() && ! other.empty() && - std::abs(this->layer->height - other.layer->height) < EPSILON && - this->layer->bridging == other.layer->bridging; - } - - // Merge regions, perform boolean union over the merged polygons. - void merge(SupportGeneratorLayerExtruded &&other) { - assert(this->could_merge(other)); - // 1) Merge the rest polygons to extrude, if there are any. - if (other.m_polygons_to_extrude != nullptr) { - if (m_polygons_to_extrude == nullptr) { - // This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). - assert(this->extrusions.empty()); - m_polygons_to_extrude = std::make_unique(this->layer->polygons); - } - Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude)); - *m_polygons_to_extrude = union_safety_offset(*m_polygons_to_extrude); - other.m_polygons_to_extrude.reset(); - } else if (m_polygons_to_extrude != nullptr) { - assert(other.m_polygons_to_extrude == nullptr); - // The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). - assert(other.extrusions.empty()); - Slic3r::polygons_append(*m_polygons_to_extrude, other.layer->polygons); - *m_polygons_to_extrude = union_safety_offset(*m_polygons_to_extrude); - } - // 2) Merge the extrusions. - this->extrusions.insert(this->extrusions.end(), other.extrusions.begin(), other.extrusions.end()); - other.extrusions.clear(); - // 3) Merge the infill polygons. - Slic3r::polygons_append(this->layer->polygons, std::move(other.layer->polygons)); - this->layer->polygons = union_safety_offset(this->layer->polygons); - other.layer->polygons.clear(); - } - - void polygons_append(Polygons &dst) const { - if (layer != NULL && ! layer->polygons.empty()) - Slic3r::polygons_append(dst, layer->polygons); - } - - // The source layer. It carries the height and extrusion type (bridging / non bridging, extrusion height). - SupportGeneratorLayer *layer { nullptr }; - // Collect extrusions. They will be exported sorted by the bottom height. - ExtrusionEntitiesPtr extrusions; - -private: - // In case the extrusions are non-empty, m_polygons_to_extrude may contain the rest areas yet to be filled by additional support. - // This is useful mainly for the loop interfaces, which are generated before the zig-zag infills. - std::unique_ptr m_polygons_to_extrude; -}; - -typedef std::vector SupportGeneratorLayerExtrudedPtrs; - -struct LoopInterfaceProcessor -{ - LoopInterfaceProcessor(coordf_t circle_r) : - n_contact_loops(0), - circle_radius(circle_r), - circle_distance(circle_r * 3.) - { - // Shape of the top contact area. - circle.points.reserve(6); - for (size_t i = 0; i < 6; ++ i) { - double angle = double(i) * M_PI / 3.; - circle.points.push_back(Point(circle_radius * cos(angle), circle_radius * sin(angle))); - } - } - - // Generate loop contacts at the top_contact_layer, - // trim the top_contact_layer->polygons with the areas covered by the loops. - void generate(SupportGeneratorLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const; - - int n_contact_loops; - coordf_t circle_radius; - coordf_t circle_distance; - Polygon circle; -}; - -void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const -{ - if (n_contact_loops == 0 || top_contact_layer.empty()) - return; - - Flow flow = interface_flow_src.with_height(top_contact_layer.layer->height); - - Polygons overhang_polygons; - if (top_contact_layer.layer->overhang_polygons != nullptr) - overhang_polygons = std::move(*top_contact_layer.layer->overhang_polygons); - - // Generate the outermost loop. - // Find centerline of the external loop (or any other kind of extrusions should the loop be skipped) - ExPolygons top_contact_expolygons = offset_ex(union_ex(top_contact_layer.layer->polygons), - 0.5f * flow.scaled_width()); - - // Grid size and bit shifts for quick and exact to/from grid coordinates manipulation. - coord_t circle_grid_resolution = 1; - coord_t circle_grid_powerof2 = 0; - { - // epsilon to account for rounding errors - coord_t circle_grid_resolution_non_powerof2 = coord_t(2. * circle_distance + 3.); - while (circle_grid_resolution < circle_grid_resolution_non_powerof2) { - circle_grid_resolution <<= 1; - ++ circle_grid_powerof2; - } - } - - struct PointAccessor { - const Point* operator()(const Point &pt) const { return &pt; } - }; - typedef ClosestPointInRadiusLookup ClosestPointLookupType; - - Polygons loops0; - { - // find centerline of the external loop of the contours - // Only consider the loops facing the overhang. - Polygons external_loops; - // Holes in the external loops. - Polygons circles; - Polygons overhang_with_margin = offset(union_ex(overhang_polygons), 0.5f * flow.scaled_width()); - for (ExPolygons::iterator it_contact_expoly = top_contact_expolygons.begin(); it_contact_expoly != top_contact_expolygons.end(); ++ it_contact_expoly) { - // Store the circle centers placed for an expolygon into a regular grid, hashed by the circle centers. - ClosestPointLookupType circle_centers_lookup(coord_t(circle_distance - SCALED_EPSILON)); - Points circle_centers; - Point center_last; - // For each contour of the expolygon, start with the outer contour, continue with the holes. - for (size_t i_contour = 0; i_contour <= it_contact_expoly->holes.size(); ++ i_contour) { - Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1]; - const Point *seg_current_pt = nullptr; - coordf_t seg_current_t = 0.; - if (! intersection_pl(contour.split_at_first_point(), overhang_with_margin).empty()) { - // The contour is below the overhang at least to some extent. - //FIXME ideally one would place the circles below the overhang only. - // Walk around the contour and place circles so their centers are not closer than circle_distance from each other. - if (circle_centers.empty()) { - // Place the first circle. - seg_current_pt = &contour.points.front(); - seg_current_t = 0.; - center_last = *seg_current_pt; - circle_centers_lookup.insert(center_last); - circle_centers.push_back(center_last); - } - for (Points::const_iterator it = contour.points.begin() + 1; it != contour.points.end(); ++it) { - // Is it possible to place a circle on this segment? Is it not too close to any of the circles already placed on this contour? - const Point &p1 = *(it-1); - const Point &p2 = *it; - // Intersection of a ray (p1, p2) with a circle placed at center_last, with radius of circle_distance. - const Vec2d v_seg(coordf_t(p2(0)) - coordf_t(p1(0)), coordf_t(p2(1)) - coordf_t(p1(1))); - const Vec2d v_cntr(coordf_t(p1(0) - center_last(0)), coordf_t(p1(1) - center_last(1))); - coordf_t a = v_seg.squaredNorm(); - coordf_t b = 2. * v_seg.dot(v_cntr); - coordf_t c = v_cntr.squaredNorm() - circle_distance * circle_distance; - coordf_t disc = b * b - 4. * a * c; - if (disc > 0.) { - // The circle intersects a ray. Avoid the parts of the segment inside the circle. - coordf_t t1 = (-b - sqrt(disc)) / (2. * a); - coordf_t t2 = (-b + sqrt(disc)) / (2. * a); - coordf_t t0 = (seg_current_pt == &p1) ? seg_current_t : 0.; - // Take the lowest t in , excluding . - coordf_t t; - if (t0 <= t1) - t = t0; - else if (t2 <= 1.) - t = t2; - else { - // Try the following segment. - seg_current_pt = nullptr; - continue; - } - seg_current_pt = &p1; - seg_current_t = t; - center_last = Point(p1(0) + coord_t(v_seg(0) * t), p1(1) + coord_t(v_seg(1) * t)); - // It has been verified that the new point is far enough from center_last. - // Ensure, that it is far enough from all the centers. - std::pair circle_closest = circle_centers_lookup.find(center_last); - if (circle_closest.first != nullptr) { - -- it; - continue; - } - } else { - // All of the segment is outside the circle. Take the first point. - seg_current_pt = &p1; - seg_current_t = 0.; - center_last = p1; - } - // Place the first circle. - circle_centers_lookup.insert(center_last); - circle_centers.push_back(center_last); - } - external_loops.push_back(std::move(contour)); - for (const Point ¢er : circle_centers) { - circles.push_back(circle); - circles.back().translate(center); - } - } - } - } - // Apply a pattern to the external loops. - loops0 = diff(external_loops, circles); - } - - Polylines loop_lines; - { - // make more loops - Polygons loop_polygons = loops0; - for (int i = 1; i < n_contact_loops; ++ i) - polygons_append(loop_polygons, - opening( - loops0, - i * flow.scaled_spacing() + 0.5f * flow.scaled_spacing(), - 0.5f * flow.scaled_spacing())); - // Clip such loops to the side oriented towards the object. - // Collect split points, so they will be recognized after the clipping. - // At the split points the clipped pieces will be stitched back together. - loop_lines.reserve(loop_polygons.size()); - std::unordered_map map_split_points; - for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it) { - assert(map_split_points.find(it->first_point()) == map_split_points.end()); - map_split_points[it->first_point()] = -1; - loop_lines.push_back(it->split_at_first_point()); - } - loop_lines = intersection_pl(loop_lines, expand(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN))); - // Because a closed loop has been split to a line, loop_lines may contain continuous segments split to 2 pieces. - // Try to connect them. - for (int i_line = 0; i_line < int(loop_lines.size()); ++ i_line) { - Polyline &polyline = loop_lines[i_line]; - auto it = map_split_points.find(polyline.first_point()); - if (it != map_split_points.end()) { - // This is a stitching point. - // If this assert triggers, multiple source polygons likely intersected at this point. - assert(it->second != -2); - if (it->second < 0) { - // First occurence. - it->second = i_line; - } else { - // Second occurence. Join the lines. - Polyline &polyline_1st = loop_lines[it->second]; - assert(polyline_1st.first_point() == it->first || polyline_1st.last_point() == it->first); - if (polyline_1st.first_point() == it->first) - polyline_1st.reverse(); - polyline_1st.append(std::move(polyline)); - it->second = -2; - } - continue; - } - it = map_split_points.find(polyline.last_point()); - if (it != map_split_points.end()) { - // This is a stitching point. - // If this assert triggers, multiple source polygons likely intersected at this point. - assert(it->second != -2); - if (it->second < 0) { - // First occurence. - it->second = i_line; - } else { - // Second occurence. Join the lines. - Polyline &polyline_1st = loop_lines[it->second]; - assert(polyline_1st.first_point() == it->first || polyline_1st.last_point() == it->first); - if (polyline_1st.first_point() == it->first) - polyline_1st.reverse(); - polyline.reverse(); - polyline_1st.append(std::move(polyline)); - it->second = -2; - } - } - } - // Remove empty lines. - remove_degenerate(loop_lines); - } - - // add the contact infill area to the interface area - // note that growing loops by $circle_radius ensures no tiny - // extrusions are left inside the circles; however it creates - // a very large gap between loops and contact_infill_polygons, so maybe another - // solution should be found to achieve both goals - // Store the trimmed polygons into a separate polygon set, so the original infill area remains intact for - // "modulate by layer thickness". - top_contact_layer.set_polygons_to_extrude(diff(top_contact_layer.layer->polygons, offset(loop_lines, float(circle_radius * 1.1)))); - - // Transform loops into ExtrusionPath objects. - extrusion_entities_append_paths( - top_contact_layer.extrusions, - std::move(loop_lines), - ExtrusionRole::SupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); -} - -#ifdef SLIC3R_DEBUG -static std::string dbg_index_to_color(int idx) -{ - if (idx < 0) - return "yellow"; - idx = idx % 3; - switch (idx) { - case 0: return "red"; - case 1: return "green"; - default: return "blue"; - } -} -#endif /* SLIC3R_DEBUG */ - -// When extruding a bottom interface layer over an object, the bottom interface layer is extruded in a thin air, therefore -// it is being extruded with a bridging flow to not shrink excessively (the die swell effect). -// Tiny extrusions are better avoided and it is always better to anchor the thread to an existing support structure if possible. -// Therefore the bottom interface spots are expanded a bit. The expanded regions may overlap with another bottom interface layers, -// leading to over extrusion, where they overlap. The over extrusion is better avoided as it often makes the interface layers -// to stick too firmly to the object. -// -// Modulate thickness (increase bottom_z) of extrusions_in_out generated for this_layer -// if they overlap with overlapping_layers, whose print_z is above this_layer.bottom_z() and below this_layer.print_z. -void modulate_extrusion_by_overlapping_layers( - // Extrusions generated for this_layer. - ExtrusionEntitiesPtr &extrusions_in_out, - const SupportGeneratorLayer &this_layer, - // Multiple layers overlapping with this_layer, sorted bottom up. - const SupportGeneratorLayersPtr &overlapping_layers) -{ - size_t n_overlapping_layers = overlapping_layers.size(); - if (n_overlapping_layers == 0 || extrusions_in_out.empty()) - // The extrusions do not overlap with any other extrusion. - return; - - // Get the initial extrusion parameters. - ExtrusionPath *extrusion_path_template = dynamic_cast(extrusions_in_out.front()); - assert(extrusion_path_template != nullptr); - ExtrusionRole extrusion_role = extrusion_path_template->role(); - float extrusion_width = extrusion_path_template->width; - - struct ExtrusionPathFragment - { - ExtrusionPathFragment() : mm3_per_mm(-1), width(-1), height(-1) {}; - ExtrusionPathFragment(double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height) {}; - - Polylines polylines; - double mm3_per_mm; - float width; - float height; - }; - - // Split the extrusions by the overlapping layers, reduce their extrusion rate. - // The last path_fragment is from this_layer. - std::vector path_fragments( - n_overlapping_layers + 1, - ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height)); - // Don't use it, it will be released. - extrusion_path_template = nullptr; - -#ifdef SLIC3R_DEBUG - static int iRun = 0; - ++ iRun; - BoundingBox bbox; - for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { - const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; - bbox.merge(get_extents(overlapping_layer.polygons)); - } - for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { - ExtrusionPath *path = dynamic_cast(*it); - assert(path != nullptr); - bbox.merge(get_extents(path->polyline)); - } - SVG svg(debug_out_path("support-fragments-%d-%lf.svg", iRun, this_layer.print_z).c_str(), bbox); - const float transparency = 0.5f; - // Filled polygons for the overlapping regions. - svg.draw(union_ex(this_layer.polygons), dbg_index_to_color(-1), transparency); - for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { - const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; - svg.draw(union_ex(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), transparency); - } - // Contours of the overlapping regions. - svg.draw(to_polylines(this_layer.polygons), dbg_index_to_color(-1), scale_(0.2)); - for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { - const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; - svg.draw(to_polylines(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), scale_(0.1)); - } - // Fill extrusion, the source. - for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { - ExtrusionPath *path = dynamic_cast(*it); - std::string color_name; - switch ((it - extrusions_in_out.begin()) % 9) { - case 0: color_name = "magenta"; break; - case 1: color_name = "deepskyblue"; break; - case 2: color_name = "coral"; break; - case 3: color_name = "goldenrod"; break; - case 4: color_name = "orange"; break; - case 5: color_name = "olivedrab"; break; - case 6: color_name = "blueviolet"; break; - case 7: color_name = "brown"; break; - default: color_name = "orchid"; break; - } - svg.draw(path->polyline, color_name, scale_(0.2)); - } -#endif /* SLIC3R_DEBUG */ - - // End points of the original paths. - std::vector> path_ends; - // Collect the paths of this_layer. - { - Polylines &polylines = path_fragments.back().polylines; - for (ExtrusionEntity *ee : extrusions_in_out) { - ExtrusionPath *path = dynamic_cast(ee); - assert(path != nullptr); - polylines.emplace_back(Polyline(std::move(path->polyline))); - path_ends.emplace_back(std::pair(polylines.back().points.front(), polylines.back().points.back())); - delete path; - } - } - // Destroy the original extrusion paths, their polylines were moved to path_fragments already. - // This will be the destination for the new paths. - extrusions_in_out.clear(); - - // Fragment the path segments by overlapping layers. The overlapping layers are sorted by an increasing print_z. - // Trim by the highest overlapping layer first. - for (int i_overlapping_layer = int(n_overlapping_layers) - 1; i_overlapping_layer >= 0; -- i_overlapping_layer) { - const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; - ExtrusionPathFragment &frag = path_fragments[i_overlapping_layer]; - Polygons polygons_trimming = offset(union_ex(overlapping_layer.polygons), float(scale_(0.5*extrusion_width))); - frag.polylines = intersection_pl(path_fragments.back().polylines, polygons_trimming); - path_fragments.back().polylines = diff_pl(path_fragments.back().polylines, polygons_trimming); - // Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter). - assert(this_layer.print_z > overlapping_layer.print_z); - frag.height = float(this_layer.print_z - overlapping_layer.print_z); - frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f).mm3_per_mm(); -#ifdef SLIC3R_DEBUG - svg.draw(frag.polylines, dbg_index_to_color(i_overlapping_layer), scale_(0.1)); -#endif /* SLIC3R_DEBUG */ - } - -#ifdef SLIC3R_DEBUG - svg.draw(path_fragments.back().polylines, dbg_index_to_color(-1), scale_(0.1)); - svg.Close(); -#endif /* SLIC3R_DEBUG */ - - // Now chain the split segments using hashing and a nearly exact match, maintaining the order of segments. - // Create a single ExtrusionPath or ExtrusionEntityCollection per source ExtrusionPath. - // Map of fragment start/end points to a pair of - // Because a non-exact matching is used for the end points, a multi-map is used. - // As the clipper library may reverse the order of some clipped paths, store both ends into the map. - struct ExtrusionPathFragmentEnd - { - ExtrusionPathFragmentEnd(size_t alayer_idx, size_t apolyline_idx, bool ais_start) : - layer_idx(alayer_idx), polyline_idx(apolyline_idx), is_start(ais_start) {} - size_t layer_idx; - size_t polyline_idx; - bool is_start; - }; - class ExtrusionPathFragmentEndPointAccessor { - public: - ExtrusionPathFragmentEndPointAccessor(const std::vector &path_fragments) : m_path_fragments(path_fragments) {} - // Return an end point of a fragment, or nullptr if the fragment has been consumed already. - const Point* operator()(const ExtrusionPathFragmentEnd &fragment_end) const { - const Polyline &polyline = m_path_fragments[fragment_end.layer_idx].polylines[fragment_end.polyline_idx]; - return polyline.points.empty() ? nullptr : - (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back()); - } - private: - ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) { - return *this; - } - - const std::vector &m_path_fragments; - }; - const coord_t search_radius = 7; - ClosestPointInRadiusLookup map_fragment_starts( - search_radius, ExtrusionPathFragmentEndPointAccessor(path_fragments)); - for (size_t i_overlapping_layer = 0; i_overlapping_layer <= n_overlapping_layers; ++ i_overlapping_layer) { - const Polylines &polylines = path_fragments[i_overlapping_layer].polylines; - for (size_t i_polyline = 0; i_polyline < polylines.size(); ++ i_polyline) { - // Map a starting point of a polyline to a pair of - if (polylines[i_polyline].points.size() >= 2) { - map_fragment_starts.insert(ExtrusionPathFragmentEnd(i_overlapping_layer, i_polyline, true)); - map_fragment_starts.insert(ExtrusionPathFragmentEnd(i_overlapping_layer, i_polyline, false)); - } - } - } - - // For each source path: - for (size_t i_path = 0; i_path < path_ends.size(); ++ i_path) { - const Point &pt_start = path_ends[i_path].first; - const Point &pt_end = path_ends[i_path].second; - Point pt_current = pt_start; - // Find a chain of fragments with the original / reduced print height. - ExtrusionMultiPath multipath; - for (;;) { - // Find a closest end point to pt_current. - std::pair end_and_dist2 = map_fragment_starts.find(pt_current); - // There may be a bug in Clipper flipping the order of two last points in a fragment? - // assert(end_and_dist2.first != nullptr); - assert(end_and_dist2.first == nullptr || end_and_dist2.second < search_radius * search_radius); - if (end_and_dist2.first == nullptr) { - // New fragment connecting to pt_current was not found. - // Verify that the last point found is close to the original end point of the unfragmented path. - //const double d2 = (pt_end - pt_current).cast.squaredNorm(); - //assert(d2 < coordf_t(search_radius * search_radius)); - // End of the path. - break; - } - const ExtrusionPathFragmentEnd &fragment_end_min = *end_and_dist2.first; - // Fragment to consume. - ExtrusionPathFragment &frag = path_fragments[fragment_end_min.layer_idx]; - Polyline &frag_polyline = frag.polylines[fragment_end_min.polyline_idx]; - // Path to append the fragment to. - ExtrusionPath *path = multipath.paths.empty() ? nullptr : &multipath.paths.back(); - if (path != nullptr) { - // Verify whether the path is compatible with the current fragment. - assert(this_layer.layer_type == SupporLayerType::BottomContact || path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm); - if (path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm) { - path = nullptr; - } - // Merging with the previous path. This can only happen if the current layer was reduced by a base layer, which was split into a base and interface layer. - } - if (path == nullptr) { - // Allocate a new path. - multipath.paths.push_back(ExtrusionPath(extrusion_role, frag.mm3_per_mm, frag.width, frag.height)); - path = &multipath.paths.back(); - } - // The Clipper library may flip the order of the clipped polylines arbitrarily. - // Reverse the source polyline, if connecting to the end. - if (! fragment_end_min.is_start) - frag_polyline.reverse(); - // Enforce exact overlap of the end points of successive fragments. - assert(frag_polyline.points.front() == pt_current); - frag_polyline.points.front() = pt_current; - // Don't repeat the first point. - if (! path->polyline.points.empty()) - path->polyline.points.pop_back(); - // Consume the fragment's polyline, remove it from the input fragments, so it will be ignored the next time. - path->polyline.append(std::move(frag_polyline)); - frag_polyline.points.clear(); - pt_current = path->polyline.points.back(); - if (pt_current == pt_end) { - // End of the path. - break; - } - } - if (!multipath.paths.empty()) { - if (multipath.paths.size() == 1) { - // This path was not fragmented. - extrusions_in_out.push_back(new ExtrusionPath(std::move(multipath.paths.front()))); - } else { - // This path was fragmented. Copy the collection as a whole object, so the order inside the collection will not be changed - // during the chaining of extrusions_in_out. - extrusions_in_out.push_back(new ExtrusionMultiPath(std::move(multipath))); - } - } - } - // If there are any non-consumed fragments, add them separately. - //FIXME this shall not happen, if the Clipper works as expected and all paths split to fragments could be re-connected. - for (auto it_fragment = path_fragments.begin(); it_fragment != path_fragments.end(); ++ it_fragment) - extrusion_entities_append_paths(extrusions_in_out, std::move(it_fragment->polylines), extrusion_role, it_fragment->mm3_per_mm, it_fragment->width, it_fragment->height); -} - -SupportGeneratorLayersPtr generate_support_layers( - PrintObject &object, - const SupportGeneratorLayersPtr &raft_layers, - const SupportGeneratorLayersPtr &bottom_contacts, - const SupportGeneratorLayersPtr &top_contacts, - const SupportGeneratorLayersPtr &intermediate_layers, - const SupportGeneratorLayersPtr &interface_layers, - const SupportGeneratorLayersPtr &base_interface_layers) -{ - // Install support layers into the object. - // A support layer installed on a PrintObject has a unique print_z. - SupportGeneratorLayersPtr layers_sorted; - layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size() + base_interface_layers.size()); - layers_append(layers_sorted, raft_layers); - layers_append(layers_sorted, bottom_contacts); - layers_append(layers_sorted, top_contacts); - layers_append(layers_sorted, intermediate_layers); - layers_append(layers_sorted, interface_layers); - layers_append(layers_sorted, base_interface_layers); - // Sort the layers lexicographically by a raising print_z and a decreasing height. - std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); - int layer_id = 0; - int layer_id_interface = 0; - assert(object.support_layers().empty()); - for (size_t i = 0; i < layers_sorted.size();) { - // Find the last layer with roughly the same print_z, find the minimum layer height of all. - // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. - size_t j = i + 1; - coordf_t zmax = layers_sorted[i]->print_z + EPSILON; - for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ; - // Assign an average print_z to the set of layers with nearly equal print_z. - coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z); - coordf_t height_min = layers_sorted[i]->height; - bool empty = true; - // For snug supports, layers where the direction of the support interface shall change are accounted for. - size_t num_interfaces = 0; - size_t num_top_contacts = 0; - double top_contact_bottom_z = 0; - for (size_t u = i; u < j; ++u) { - SupportGeneratorLayer &layer = *layers_sorted[u]; - if (! layer.polygons.empty()) { - empty = false; - num_interfaces += one_of(layer.layer_type, support_types_interface); - if (layer.layer_type == SupporLayerType::TopContact) { - ++ num_top_contacts; - assert(num_top_contacts <= 1); - // All top contact layers sharing this print_z shall also share bottom_z. - //assert(num_top_contacts == 1 || (top_contact_bottom_z - layer.bottom_z) < EPSILON); - top_contact_bottom_z = layer.bottom_z; - } - } - layer.print_z = zavg; - height_min = std::min(height_min, layer.height); - } - if (! empty) { - // Here the upper_layer and lower_layer pointers are left to null at the support layers, - // as they are never used. These pointers are candidates for removal. - bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; - size_t this_layer_id_interface = layer_id_interface; - if (this_layer_contacts_only) { - // Find a supporting layer for its interface ID. - for (auto it = object.support_layers().rbegin(); it != object.support_layers().rend(); ++ it) - if (const SupportLayer &other_layer = **it; std::abs(other_layer.print_z - top_contact_bottom_z) < EPSILON) { - // other_layer supports this top contact layer. Assign a different support interface direction to this layer - // from the layer that supports it. - this_layer_id_interface = other_layer.interface_id() + 1; - } - } - object.add_support_layer(layer_id ++, this_layer_id_interface, height_min, zavg); - if (num_interfaces && ! this_layer_contacts_only) - ++ layer_id_interface; - } - i = j; - } - return layers_sorted; -} - -void generate_support_toolpaths( - SupportLayerPtrs &support_layers, - const PrintObjectConfig &config, - const SupportParameters &support_params, - const SlicingParameters &slicing_params, - const SupportGeneratorLayersPtr &raft_layers, - const SupportGeneratorLayersPtr &bottom_contacts, - const SupportGeneratorLayersPtr &top_contacts, - const SupportGeneratorLayersPtr &intermediate_layers, - const SupportGeneratorLayersPtr &interface_layers, - const SupportGeneratorLayersPtr &base_interface_layers) -{ - // loop_interface_processor with a given circle radius. - LoopInterfaceProcessor loop_interface_processor(1.5 * support_params.support_material_interface_flow.scaled_width()); - loop_interface_processor.n_contact_loops = config.support_material_interface_contact_loops ? 1 : 0; - - std::vector angles { support_params.base_angle }; - if (config.support_material_pattern == smpRectilinearGrid) - angles.push_back(support_params.interface_angle); - - BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); - -// const coordf_t link_max_length_factor = 3.; - const coordf_t link_max_length_factor = 0.; - - // Insert the raft base layers. - auto n_raft_layers = std::min(support_layers.size(), std::max(0, int(slicing_params.raft_layers()) - 1)); - - tbb::parallel_for(tbb::blocked_range(0, n_raft_layers), - [&support_layers, &raft_layers, &intermediate_layers, &config, &support_params, &slicing_params, - &bbox_object, link_max_length_factor] - (const tbb::blocked_range& range) { - for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) - { - assert(support_layer_id < raft_layers.size()); - SupportLayer &support_layer = *support_layers[support_layer_id]; - assert(support_layer.support_fills.entities.empty()); - SupportGeneratorLayer &raft_layer = *raft_layers[support_layer_id]; - - std::unique_ptr filler_interface = std::unique_ptr(Fill::new_from_type(support_params.raft_interface_fill_pattern)); - std::unique_ptr filler_support = std::unique_ptr(Fill::new_from_type(support_params.base_fill_pattern)); - filler_interface->set_bounding_box(bbox_object); - filler_support->set_bounding_box(bbox_object); - - // Print the tree supports cutting through the raft with the exception of the 1st layer, where a full support layer will be printed below - // both the raft and the trees. - // Trim the raft layers with the tree polygons. - const Polygons &tree_polygons = - support_layer_id > 0 && support_layer_id < intermediate_layers.size() && is_approx(intermediate_layers[support_layer_id]->print_z, support_layer.print_z) ? - intermediate_layers[support_layer_id]->polygons : Polygons(); - - // Print the support base below the support columns, or the support base for the support columns plus the contacts. - if (support_layer_id > 0) { - const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? - raft_layer.polygons : - //FIXME misusing contact_polygons for support columns. - ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); - // Trees may cut through the raft layers down to a print bed. - Flow flow(float(support_params.support_material_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter()); - assert(!raft_layer.bridging); - if (! to_infill_polygons.empty()) { - Fill *filler = filler_support.get(); - filler->angle = support_params.raft_angle_base; - filler->spacing = support_params.support_material_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density)); - fill_expolygons_with_sheath_generate_paths( - // Destination - support_layer.support_fills.entities, - // Regions to fill - tree_polygons.empty() ? to_infill_polygons : diff(to_infill_polygons, tree_polygons), - // Filler and its parameters - filler, float(support_params.support_density), - // Extrusion parameters - ExtrusionRole::SupportMaterial, flow, - support_params.with_sheath, false); - } - if (! tree_polygons.empty()) - tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow); - } - - Fill *filler = filler_interface.get(); - Flow flow = support_params.first_layer_flow; - float density = 0.f; - if (support_layer_id == 0) { - // Base flange. - filler->angle = support_params.raft_angle_1st_layer; - filler->spacing = support_params.first_layer_flow.spacing(); - density = float(config.raft_first_layer_density.value * 0.01); - } else if (support_layer_id >= slicing_params.base_raft_layers) { - filler->angle = support_params.raft_interface_angle(support_layer.interface_id()); - // We don't use $base_flow->spacing because we need a constant spacing - // value that guarantees that all layers are correctly aligned. - filler->spacing = support_params.support_material_flow.spacing(); - assert(! raft_layer.bridging); - flow = Flow(float(support_params.raft_interface_flow.width()), float(raft_layer.height), support_params.raft_interface_flow.nozzle_diameter()); - density = float(support_params.raft_interface_density); - } else - continue; - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); - fill_expolygons_with_sheath_generate_paths( - // Destination - support_layer.support_fills.entities, - // Regions to fill - tree_polygons.empty() ? raft_layer.polygons : diff(raft_layer.polygons, tree_polygons), - // Filler and its parameters - filler, density, - // Extrusion parameters - (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface, flow, - // sheath at first layer - support_layer_id == 0, support_layer_id == 0); - } - }); - - struct LayerCacheItem { - LayerCacheItem(SupportGeneratorLayerExtruded *layer_extruded = nullptr) : layer_extruded(layer_extruded) {} - SupportGeneratorLayerExtruded *layer_extruded; - std::vector overlapping; - }; - struct LayerCache { - SupportGeneratorLayerExtruded bottom_contact_layer; - SupportGeneratorLayerExtruded top_contact_layer; - SupportGeneratorLayerExtruded base_layer; - SupportGeneratorLayerExtruded interface_layer; - SupportGeneratorLayerExtruded base_interface_layer; - boost::container::static_vector nonempty; - - void add_nonempty_and_sort() { - for (SupportGeneratorLayerExtruded *item : { &bottom_contact_layer, &top_contact_layer, &interface_layer, &base_interface_layer, &base_layer }) - if (! item->empty()) - this->nonempty.emplace_back(item); - // Sort the layers with the same print_z coordinate by their heights, thickest first. - std::stable_sort(this->nonempty.begin(), this->nonempty.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; }); - } - }; - std::vector layer_caches(support_layers.size()); - - tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), - [&config, &slicing_params, &support_params, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &base_interface_layers, &layer_caches, &loop_interface_processor, - &bbox_object, &angles, n_raft_layers, link_max_length_factor] - (const tbb::blocked_range& range) { - // Indices of the 1st layer in their respective container at the support layer height. - size_t idx_layer_bottom_contact = size_t(-1); - size_t idx_layer_top_contact = size_t(-1); - size_t idx_layer_intermediate = size_t(-1); - size_t idx_layer_interface = size_t(-1); - size_t idx_layer_base_interface = size_t(-1); - const auto fill_type_first_layer = ipRectilinear; - auto filler_interface = std::unique_ptr(Fill::new_from_type(support_params.contact_fill_pattern)); - // Filler for the 1st layer interface, if different from filler_interface. - auto filler_first_layer_ptr = std::unique_ptr(range.begin() == 0 && support_params.contact_fill_pattern != fill_type_first_layer ? Fill::new_from_type(fill_type_first_layer) : nullptr); - // Pointer to the 1st layer interface filler. - auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get(); - // Filler for the 1st layer interface, if different from filler_interface. - auto filler_raft_contact_ptr = std::unique_ptr(range.begin() == n_raft_layers && config.support_material_interface_layers.value == 0 ? - Fill::new_from_type(support_params.raft_interface_fill_pattern) : nullptr); - // Pointer to the 1st layer interface filler. - auto filler_raft_contact = filler_raft_contact_ptr ? filler_raft_contact_ptr.get() : filler_interface.get(); - // Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer). - auto filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : - Fill::new_from_type(support_params.interface_density > 0.95 || support_params.with_sheath ? ipRectilinear : ipSupportBase)); - auto filler_support = std::unique_ptr(Fill::new_from_type(support_params.base_fill_pattern)); - filler_interface->set_bounding_box(bbox_object); - if (filler_first_layer_ptr) - filler_first_layer_ptr->set_bounding_box(bbox_object); - if (filler_raft_contact_ptr) - filler_raft_contact_ptr->set_bounding_box(bbox_object); - if (filler_base_interface) - filler_base_interface->set_bounding_box(bbox_object); - filler_support->set_bounding_box(bbox_object); - for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) - { - SupportLayer &support_layer = *support_layers[support_layer_id]; - LayerCache &layer_cache = layer_caches[support_layer_id]; - const float support_interface_angle = config.support_material_style.value == smsGrid ? - support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id()); - - // Find polygons with the same print_z. - SupportGeneratorLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer; - SupportGeneratorLayerExtruded &top_contact_layer = layer_cache.top_contact_layer; - SupportGeneratorLayerExtruded &base_layer = layer_cache.base_layer; - SupportGeneratorLayerExtruded &interface_layer = layer_cache.interface_layer; - SupportGeneratorLayerExtruded &base_interface_layer = layer_cache.base_interface_layer; - // Increment the layer indices to find a layer at support_layer.print_z. - { - auto fun = [&support_layer](const SupportGeneratorLayer *l){ return l->print_z >= support_layer.print_z - EPSILON; }; - idx_layer_bottom_contact = idx_higher_or_equal(bottom_contacts, idx_layer_bottom_contact, fun); - idx_layer_top_contact = idx_higher_or_equal(top_contacts, idx_layer_top_contact, fun); - idx_layer_intermediate = idx_higher_or_equal(intermediate_layers, idx_layer_intermediate, fun); - idx_layer_interface = idx_higher_or_equal(interface_layers, idx_layer_interface, fun); - idx_layer_base_interface = idx_higher_or_equal(base_interface_layers, idx_layer_base_interface,fun); - } - // Copy polygons from the layers. - if (idx_layer_bottom_contact < bottom_contacts.size() && bottom_contacts[idx_layer_bottom_contact]->print_z < support_layer.print_z + EPSILON) - bottom_contact_layer.layer = bottom_contacts[idx_layer_bottom_contact]; - if (idx_layer_top_contact < top_contacts.size() && top_contacts[idx_layer_top_contact]->print_z < support_layer.print_z + EPSILON) - top_contact_layer.layer = top_contacts[idx_layer_top_contact]; - if (idx_layer_interface < interface_layers.size() && interface_layers[idx_layer_interface]->print_z < support_layer.print_z + EPSILON) - interface_layer.layer = interface_layers[idx_layer_interface]; - if (idx_layer_base_interface < base_interface_layers.size() && base_interface_layers[idx_layer_base_interface]->print_z < support_layer.print_z + EPSILON) - base_interface_layer.layer = base_interface_layers[idx_layer_base_interface]; - if (idx_layer_intermediate < intermediate_layers.size() && intermediate_layers[idx_layer_intermediate]->print_z < support_layer.print_z + EPSILON) - base_layer.layer = intermediate_layers[idx_layer_intermediate]; - - // This layer is a raft contact layer. Any contact polygons at this layer are raft contacts. - bool raft_layer = slicing_params.interface_raft_layers && top_contact_layer.layer && is_approx(top_contact_layer.layer->print_z, slicing_params.raft_contact_top_z); - if (config.support_material_interface_layers == 0) { - // If no top interface layers were requested, we treat the contact layer exactly as a generic base layer. - // Don't merge the raft contact layer though. - if (support_params.can_merge_support_regions && ! raft_layer) { - if (base_layer.could_merge(top_contact_layer)) - base_layer.merge(std::move(top_contact_layer)); - else if (base_layer.empty()) - base_layer = std::move(top_contact_layer); - } - } else { - loop_interface_processor.generate(top_contact_layer, support_params.support_material_interface_flow); - // If no loops are allowed, we treat the contact layer exactly as a generic interface layer. - // Merge interface_layer into top_contact_layer, as the top_contact_layer is not synchronized and therefore it will be used - // to trim other layers. - if (top_contact_layer.could_merge(interface_layer) && ! raft_layer) - top_contact_layer.merge(std::move(interface_layer)); - } - if ((config.support_material_interface_layers == 0 || config.support_material_bottom_interface_layers == 0) && support_params.can_merge_support_regions) { - if (base_layer.could_merge(bottom_contact_layer)) - base_layer.merge(std::move(bottom_contact_layer)); - else if (base_layer.empty() && ! bottom_contact_layer.empty() && ! bottom_contact_layer.layer->bridging) - base_layer = std::move(bottom_contact_layer); - } else if (bottom_contact_layer.could_merge(top_contact_layer) && ! raft_layer) - top_contact_layer.merge(std::move(bottom_contact_layer)); - else if (bottom_contact_layer.could_merge(interface_layer)) - bottom_contact_layer.merge(std::move(interface_layer)); - -#if 0 - if ( ! interface_layer.empty() && ! base_layer.empty()) { - // turn base support into interface when it's contained in our holes - // (this way we get wider interface anchoring) - //FIXME The intention of the code below is unclear. One likely wanted to just merge small islands of base layers filling in the holes - // inside interface layers, but the code below fills just too much, see GH #4570 - Polygons islands = top_level_islands(interface_layer.layer->polygons); - polygons_append(interface_layer.layer->polygons, intersection(base_layer.layer->polygons, islands)); - base_layer.layer->polygons = diff(base_layer.layer->polygons, islands); - } -#endif - - // Top and bottom contacts, interface layers. - enum class InterfaceLayerType { TopContact, BottomContact, RaftContact, Interface, InterfaceAsBase }; - auto extrude_interface = [&](SupportGeneratorLayerExtruded &layer_ex, InterfaceLayerType interface_layer_type) { - if (! layer_ex.empty() && ! layer_ex.polygons_to_extrude().empty()) { - bool interface_as_base = interface_layer_type == InterfaceLayerType::InterfaceAsBase; - bool raft_contact = interface_layer_type == InterfaceLayerType::RaftContact; - //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore - // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) - auto *filler = raft_contact ? filler_raft_contact : filler_interface.get(); - auto interface_flow = layer_ex.layer->bridging ? - Flow::bridging_flow(layer_ex.layer->height, support_params.support_material_bottom_interface_flow.nozzle_diameter()) : - (raft_contact ? &support_params.raft_interface_flow : - interface_as_base ? &support_params.support_material_flow : &support_params.support_material_interface_flow) - ->with_height(float(layer_ex.layer->height)); - filler->angle = interface_as_base ? - // If zero interface layers are configured, use the same angle as for the base layers. - angles[support_layer_id % angles.size()] : - // Use interface angle for the interface layers. - raft_contact ? - support_params.raft_interface_angle(support_layer.interface_id()) : - support_interface_angle; - double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density; - filler->spacing = raft_contact ? support_params.raft_interface_flow.spacing() : - interface_as_base ? support_params.support_material_flow.spacing() : support_params.support_material_interface_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); - fill_expolygons_generate_paths( - // Destination - layer_ex.extrusions, - // Regions to fill - union_safety_offset_ex(layer_ex.polygons_to_extrude()), - // Filler and its parameters - filler, float(density), - // Extrusion parameters - ExtrusionRole::SupportMaterialInterface, interface_flow); - } - }; - const bool top_interfaces = config.support_material_interface_layers.value != 0; - const bool bottom_interfaces = top_interfaces && config.support_material_bottom_interface_layers != 0; - extrude_interface(top_contact_layer, raft_layer ? InterfaceLayerType::RaftContact : top_interfaces ? InterfaceLayerType::TopContact : InterfaceLayerType::InterfaceAsBase); - extrude_interface(bottom_contact_layer, bottom_interfaces ? InterfaceLayerType::BottomContact : InterfaceLayerType::InterfaceAsBase); - extrude_interface(interface_layer, top_interfaces ? InterfaceLayerType::Interface : InterfaceLayerType::InterfaceAsBase); - - // Base interface layers under soluble interfaces - if ( ! base_interface_layer.empty() && ! base_interface_layer.polygons_to_extrude().empty()) { - Fill *filler = filler_base_interface.get(); - //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore - // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) - assert(! base_interface_layer.layer->bridging); - Flow interface_flow = support_params.support_material_flow.with_height(float(base_interface_layer.layer->height)); - filler->angle = support_interface_angle; - filler->spacing = support_params.support_material_interface_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.interface_density)); - fill_expolygons_generate_paths( - // Destination - base_interface_layer.extrusions, - //base_layer_interface.extrusions, - // Regions to fill - union_safety_offset_ex(base_interface_layer.polygons_to_extrude()), - // Filler and its parameters - filler, float(support_params.interface_density), - // Extrusion parameters - ExtrusionRole::SupportMaterial, interface_flow); - } - - // Base support or flange. - if (! base_layer.empty() && ! base_layer.polygons_to_extrude().empty()) { - Fill *filler = filler_support.get(); - filler->angle = angles[support_layer_id % angles.size()]; - // We don't use $base_flow->spacing because we need a constant spacing - // value that guarantees that all layers are correctly aligned. - assert(! base_layer.layer->bridging); - auto flow = support_params.support_material_flow.with_height(float(base_layer.layer->height)); - filler->spacing = support_params.support_material_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density)); - float density = float(support_params.support_density); - bool sheath = support_params.with_sheath; - bool no_sort = false; - bool done = false; - if (base_layer.layer->bottom_z < EPSILON) { - // Base flange (the 1st layer). - filler = filler_first_layer; - filler->angle = Geometry::deg2rad(float(config.support_material_angle.value + 90.)); - density = float(config.raft_first_layer_density.value * 0.01); - flow = support_params.first_layer_flow; - // use the proper spacing for first layer as we don't need to align - // its pattern to the other layers - //FIXME When paralellizing, each thread shall have its own copy of the fillers. - filler->spacing = flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); - sheath = true; - no_sort = true; - } else if (config.support_material_style == SupportMaterialStyle::smsOrganic) { - tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow); - done = true; - } - if (! done) - fill_expolygons_with_sheath_generate_paths( - // Destination - base_layer.extrusions, - // Regions to fill - base_layer.polygons_to_extrude(), - // Filler and its parameters - filler, density, - // Extrusion parameters - ExtrusionRole::SupportMaterial, flow, - sheath, no_sort); - } - - // Merge base_interface_layers to base_layers to avoid unneccessary retractions - if (! base_layer.empty() && ! base_interface_layer.empty() && ! base_layer.polygons_to_extrude().empty() && ! base_interface_layer.polygons_to_extrude().empty() && - base_layer.could_merge(base_interface_layer)) - base_layer.merge(std::move(base_interface_layer)); - - layer_cache.add_nonempty_and_sort(); - - // Collect the support areas with this print_z into islands, as there is no need - // for retraction over these islands. - Polygons polys; - // Collect the extrusions, sorted by the bottom extrusion height. - for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) { - // Collect islands to polys. - layer_cache_item.layer_extruded->polygons_append(polys); - // The print_z of the top contact surfaces and bottom_z of the bottom contact surfaces are "free" - // in a sense that they are not synchronized with other support layers. As the top and bottom contact surfaces - // are inflated to achieve a better anchoring, it may happen, that these surfaces will at least partially - // overlap in Z with another support layers, leading to over-extrusion. - // Mitigate the over-extrusion by modulating the extrusion rate over these regions. - // The print head will follow the same print_z, but the layer thickness will be reduced - // where it overlaps with another support layer. - //FIXME When printing a briging path, what is an equivalent height of the squished extrudate of the same width? - // Collect overlapping top/bottom surfaces. - layer_cache_item.overlapping.reserve(20); - coordf_t bottom_z = layer_cache_item.layer_extruded->layer->bottom_print_z() + EPSILON; - auto add_overlapping = [&layer_cache_item, bottom_z](const SupportGeneratorLayersPtr &layers, size_t idx_top) { - for (int i = int(idx_top) - 1; i >= 0 && layers[i]->print_z > bottom_z; -- i) - layer_cache_item.overlapping.push_back(layers[i]); - }; - add_overlapping(top_contacts, idx_layer_top_contact); - if (layer_cache_item.layer_extruded->layer->layer_type == SupporLayerType::BottomContact) { - // Bottom contact layer may overlap with a base layer, which may be changed to interface layer. - add_overlapping(intermediate_layers, idx_layer_intermediate); - add_overlapping(interface_layers, idx_layer_interface); - add_overlapping(base_interface_layers, idx_layer_base_interface); - } - // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. - std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); - } - assert(support_layer.support_islands.empty()); - if (! polys.empty()) { - support_layer.support_islands = union_ex(polys); - support_layer.support_islands_bboxes.reserve(support_layer.support_islands.size()); - for (const ExPolygon &expoly : support_layer.support_islands) - support_layer.support_islands_bboxes.emplace_back(get_extents(expoly).inflated(SCALED_EPSILON)); - } - } // for each support_layer_id - }); - - // Now modulate the support layer height in parallel. - tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), - [&support_layers, &layer_caches] - (const tbb::blocked_range& range) { - for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { - SupportLayer &support_layer = *support_layers[support_layer_id]; - LayerCache &layer_cache = layer_caches[support_layer_id]; - // For all extrusion types at this print_z, ordered by decreasing layer height: - for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) { - // Trim the extrusion height from the bottom by the overlapping layers. - modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping); - support_layer.support_fills.append(std::move(layer_cache_item.layer_extruded->extrusions)); - } - } - }); - -#ifndef NDEBUG - struct Test { - static bool verify_nonempty(const ExtrusionEntityCollection *collection) { - for (const ExtrusionEntity *ee : collection->entities) { - if (const ExtrusionPath *path = dynamic_cast(ee)) - assert(! path->empty()); - else if (const ExtrusionMultiPath *multipath = dynamic_cast(ee)) - assert(! multipath->empty()); - else if (const ExtrusionEntityCollection *eecol = dynamic_cast(ee)) { - assert(! eecol->empty()); - return verify_nonempty(eecol); - } else - assert(false); - } - return true; - } - }; - for (const SupportLayer *support_layer : support_layers) - assert(Test::verify_nonempty(&support_layer->support_fills)); -#endif // NDEBUG -} - /* void PrintObjectSupportMaterial::clip_by_pillars( const PrintObject &object, diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index 24dc8507e7..bbb991c3e6 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -12,54 +12,6 @@ namespace Slic3r { class PrintObject; -// Remove bridges from support contact areas. -// To be called if PrintObjectConfig::dont_support_bridges. -void remove_bridges_from_contacts( - const PrintConfig &print_config, - const Layer &lower_layer, - const LayerRegion &layerm, - float fw, - Polygons &contact_polygons); - -// Generate raft layers, also expand the 1st support layer -// in case there is no raft layer to improve support adhesion. -SupportGeneratorLayersPtr generate_raft_base( - const PrintObject &object, - const SupportParameters &support_params, - const SlicingParameters &slicing_params, - const SupportGeneratorLayersPtr &top_contacts, - const SupportGeneratorLayersPtr &interface_layers, - const SupportGeneratorLayersPtr &base_interface_layers, - const SupportGeneratorLayersPtr &base_layers, - SupportGeneratorLayerStorage &layer_storage); - -// returns sorted layers -SupportGeneratorLayersPtr generate_support_layers( - PrintObject &object, - const SupportGeneratorLayersPtr &raft_layers, - const SupportGeneratorLayersPtr &bottom_contacts, - const SupportGeneratorLayersPtr &top_contacts, - const SupportGeneratorLayersPtr &intermediate_layers, - const SupportGeneratorLayersPtr &interface_layers, - const SupportGeneratorLayersPtr &base_interface_layers); - -// Produce the support G-code. -// Used by both classic and tree supports. -void generate_support_toolpaths( - SupportLayerPtrs &support_layers, - const PrintObjectConfig &config, - const SupportParameters &support_params, - const SlicingParameters &slicing_params, - const SupportGeneratorLayersPtr &raft_layers, - const SupportGeneratorLayersPtr &bottom_contacts, - const SupportGeneratorLayersPtr &top_contacts, - const SupportGeneratorLayersPtr &intermediate_layers, - const SupportGeneratorLayersPtr &interface_layers, - const SupportGeneratorLayersPtr &base_interface_layers); - -void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers); -void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer); - // This class manages raft and supports for a single PrintObject. // Instantiated by Slic3r::Print::Object->_support_material() // This class is instantiated before the slicing starts as Object.pm will query @@ -84,6 +36,10 @@ public: void generate(PrintObject &object); private: + using SupportGeneratorLayersPtr = FFFSupport::SupportGeneratorLayersPtr; + using SupportGeneratorLayerStorage = FFFSupport::SupportGeneratorLayerStorage; + using SupportParameters = FFFSupport::SupportParameters; + std::vector buildplate_covered(const PrintObject &object) const; // Generate top contact layers supporting overhangs. diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 9a7b203c60..a3264990aa 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -19,9 +19,10 @@ #include "Polygon.hpp" #include "Polyline.hpp" #include "MutablePolygon.hpp" -#include "SupportMaterial.hpp" #include "TriangleMeshSlicer.hpp" +#include "Support/SupportCommon.hpp" + #include #include #include @@ -37,7 +38,6 @@ #include #include -#include #if defined(TREE_SUPPORT_SHOW_ERRORS) && defined(_WIN32) #define TREE_SUPPORT_SHOW_ERRORS_WIN32 @@ -53,6 +53,8 @@ // #define TREESUPPORT_DEBUG_SVG +using namespace Slic3r::FFFSupport; + namespace Slic3r { @@ -962,13 +964,11 @@ static LayerIndex layer_idx_floor(const SlicingParameters &slicing_params, const } static inline SupportGeneratorLayer& layer_initialize( - SupportGeneratorLayer &layer_new, - const SupporLayerType layer_type, - const SlicingParameters &slicing_params, + SupportGeneratorLayer &layer_new, + const SlicingParameters &slicing_params, const TreeSupportSettings &config, - const size_t layer_idx) + const size_t layer_idx) { - layer_new.layer_type = layer_type; layer_new.print_z = layer_z(slicing_params, config, layer_idx); layer_new.bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0; layer_new.height = layer_new.print_z - layer_new.bottom_z; @@ -976,29 +976,26 @@ static inline SupportGeneratorLayer& layer_initialize( } // Using the std::deque as an allocator. -inline SupportGeneratorLayer& layer_allocate( - std::deque &layer_storage, +inline SupportGeneratorLayer& layer_allocate_unguarded( + SupportGeneratorLayerStorage &layer_storage, SupporLayerType layer_type, const SlicingParameters &slicing_params, const TreeSupportSettings &config, size_t layer_idx) { - //FIXME take raft into account. - layer_storage.push_back(SupportGeneratorLayer()); - return layer_initialize(layer_storage.back(), layer_type, slicing_params, config, layer_idx); + SupportGeneratorLayer &layer = layer_storage.allocate_unguarded(layer_type); + return layer_initialize(layer, slicing_params, config, layer_idx); } inline SupportGeneratorLayer& layer_allocate( - std::deque &layer_storage, - tbb::spin_mutex& layer_storage_mutex, + SupportGeneratorLayerStorage &layer_storage, SupporLayerType layer_type, const SlicingParameters &slicing_params, const TreeSupportSettings &config, size_t layer_idx) { - tbb::spin_mutex::scoped_lock lock(layer_storage_mutex); - layer_storage.push_back(SupportGeneratorLayer()); - return layer_initialize(layer_storage.back(), layer_type, slicing_params, config, layer_idx); + SupportGeneratorLayer &layer = layer_storage.allocate(layer_type); + return layer_initialize(layer, slicing_params, config, layer_idx); } int generate_raft_contact( @@ -1016,7 +1013,7 @@ int generate_raft_contact( while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON) -- raft_contact_layer_idx; // Create the raft contact layer. - SupportGeneratorLayer &raft_contact_layer = layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx); + SupportGeneratorLayer &raft_contact_layer = layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx); top_contacts[raft_contact_layer_idx] = &raft_contact_layer; const ExPolygons &lslices = print_object.get_layer(0)->lslices; double expansion = print_object.config().raft_expansion.value; @@ -1115,7 +1112,7 @@ public: { SupportGeneratorLayer*& l = top_contacts[insert_layer_idx]; if (l == nullptr) - l = &layer_allocate(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, insert_layer_idx); + l = &layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, insert_layer_idx); // will be unioned in finalize_interface_and_support_areas() append(l->polygons, std::move(new_roofs)); } @@ -1141,7 +1138,7 @@ public: std::lock_guard lock(m_mutex_layer_storage); SupportGeneratorLayer*& l = top_contacts[0]; if (l == nullptr) - l = &layer_allocate(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, 0); + l = &layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, 0); append(l->polygons, std::move(overhang_areas)); } @@ -3309,7 +3306,6 @@ static void finalize_interface_and_support_areas( #endif // SLIC3R_TREESUPPORTS_PROGRESS // Iterate over the generated circles in parallel and clean them up. Also add support floor. - tbb::spin_mutex layer_storage_mutex; tbb::parallel_for(tbb::blocked_range(0, support_layer_storage.size()), [&](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { @@ -3402,7 +3398,7 @@ static void finalize_interface_and_support_areas( } if (! floor_layer.empty()) { if (support_bottom == nullptr) - support_bottom = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx); + support_bottom = &layer_allocate(layer_storage, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx); support_bottom->polygons = union_(floor_layer, support_bottom->polygons); base_layer_polygons = diff_clipped(base_layer_polygons, offset(support_bottom->polygons, scaled(0.01), jtMiter, 1.2)); // Subtract the support floor from the normal support. } @@ -3410,11 +3406,11 @@ static void finalize_interface_and_support_areas( if (! support_roof_polygons.empty()) { if (support_roof == nullptr) - support_roof = top_contacts[layer_idx] = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::TopContact, print_object.slicing_parameters(), config, layer_idx); + support_roof = top_contacts[layer_idx] = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, layer_idx); support_roof->polygons = union_(support_roof_polygons); } if (! base_layer_polygons.empty()) { - SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx); + SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(layer_storage, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx); base_layer->polygons = union_(base_layer_polygons); } From 287304214828288ede6c76073b0ba12589b8e7c1 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 5 May 2023 13:52:20 +0200 Subject: [PATCH 62/70] SPE-1684: Fix issue where the automatic painting did not trigger paint-on-supports notification in the right panel --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 7700e1eef7..557cdee8e3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -434,18 +434,15 @@ void GLGizmoFdmSupports::apply_data_from_backend() mesh_id++; auto selector = selectors.find(mv->id().id); if (selector != selectors.end()) { - mv->supported_facets.set(selector->second.selector); - m_triangle_selectors[mesh_id]->deserialize(mv->supported_facets.get_data(), true); + m_triangle_selectors[mesh_id]->deserialize(selector->second.selector.serialize(), true); m_triangle_selectors[mesh_id]->request_update_render_data(); } } } - - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); - m_parent.set_as_dirty(); } - this->waiting_for_autogenerated_supports = false; } + this->waiting_for_autogenerated_supports = false; + update_model_object(); } void GLGizmoFdmSupports::update_model_object() const From ad203baf77d152c25d718bf270df3d5dc38991fe Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 5 May 2023 14:05:22 +0200 Subject: [PATCH 63/70] Moved FFF support code to libslic3r/Support directory --- src/libslic3r/CMakeLists.txt | 12 +++++----- src/libslic3r/Print.cpp | 1 - src/libslic3r/PrintObject.cpp | 6 ++--- src/libslic3r/Support/SupportCommon.cpp | 4 ++-- src/libslic3r/Support/SupportDebug.cpp | 6 ++--- .../{ => Support}/SupportMaterial.cpp | 22 +++++++++---------- .../{ => Support}/SupportMaterial.hpp | 10 ++++----- .../{ => Support}/TreeModelVolumes.cpp | 18 +++++++-------- .../{ => Support}/TreeModelVolumes.hpp | 6 ++--- src/libslic3r/{ => Support}/TreeSupport.cpp | 0 src/libslic3r/{ => Support}/TreeSupport.hpp | 0 11 files changed, 42 insertions(+), 43 deletions(-) rename src/libslic3r/{ => Support}/SupportMaterial.cpp (99%) rename src/libslic3r/{ => Support}/SupportMaterial.hpp (97%) rename src/libslic3r/{ => Support}/TreeModelVolumes.cpp (99%) rename src/libslic3r/{ => Support}/TreeModelVolumes.hpp (99%) rename src/libslic3r/{ => Support}/TreeSupport.cpp (100%) rename src/libslic3r/{ => Support}/TreeSupport.hpp (100%) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 11f7adf766..d44bfcde09 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -281,10 +281,14 @@ set(SLIC3R_SOURCES Support/SupportDebug.cpp Support/SupportDebug.hpp Support/SupportLayer.hpp + Support/SupportMaterial.cpp + Support/SupportMaterial.hpp Support/SupportParameters.cpp Support/SupportParameters.hpp - SupportMaterial.cpp - SupportMaterial.hpp + Support/TreeSupport.cpp + Support/TreeSupport.hpp + Support/TreeModelVolumes.cpp + Support/TreeModelVolumes.hpp SupportSpotsGenerator.cpp SupportSpotsGenerator.hpp Surface.cpp @@ -298,10 +302,6 @@ set(SLIC3R_SOURCES Tesselate.cpp Tesselate.hpp TextConfiguration.hpp - TreeSupport.cpp - TreeSupport.hpp - TreeModelVolumes.cpp - TreeModelVolumes.hpp TriangleMesh.cpp TriangleMesh.hpp TriangleMeshSlicer.cpp diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index cf8b5c577d..5b23dc2606 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -8,7 +8,6 @@ #include "Geometry/ConvexHull.hpp" #include "I18N.hpp" #include "ShortestPath.hpp" -#include "SupportMaterial.hpp" #include "Thread.hpp" #include "GCode.hpp" #include "GCode/WipeTower.hpp" diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 46588b9b02..b43afd6be9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -17,8 +17,8 @@ #include "MutablePolygon.hpp" #include "PrintBase.hpp" #include "PrintConfig.hpp" -#include "SupportMaterial.hpp" -#include "TreeSupport.hpp" +#include "Support/SupportMaterial.hpp" +#include "Support/TreeSupport.hpp" #include "Surface.hpp" #include "Slicing.hpp" #include "Tesselate.hpp" @@ -27,7 +27,7 @@ #include "Fill/FillAdaptive.hpp" #include "Fill/FillLightning.hpp" #include "Format/STL.hpp" -#include "SupportMaterial.hpp" +#include "Support/SupportMaterial.hpp" #include "SupportSpotsGenerator.hpp" #include "TriangleSelectorWrapper.hpp" #include "format.hpp" diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 8825d8dd23..f76ebaa0ed 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -22,8 +22,8 @@ #define DEBUG #define _DEBUG #undef NDEBUG - #include "utils.hpp" - #include "SVG.hpp" + #include "../utils.hpp" + #include "../SVG.hpp" #endif #include diff --git a/src/libslic3r/Support/SupportDebug.cpp b/src/libslic3r/Support/SupportDebug.cpp index 8cec806c18..5c18bc7694 100644 --- a/src/libslic3r/Support/SupportDebug.cpp +++ b/src/libslic3r/Support/SupportDebug.cpp @@ -1,9 +1,9 @@ #if 1 //#ifdef SLIC3R_DEBUG -#include "ClipperUtils.hpp" -#include "SVG.hpp" - +#include "../ClipperUtils.hpp" +#include "../SVG.hpp" #include "../Layer.hpp" + #include "SupportLayer.hpp" namespace Slic3r::FFFSupport { diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp similarity index 99% rename from src/libslic3r/SupportMaterial.cpp rename to src/libslic3r/Support/SupportMaterial.cpp index 40221ec2a0..347278911f 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1,14 +1,14 @@ -#include "ClipperUtils.hpp" -#include "ExtrusionEntityCollection.hpp" -#include "Layer.hpp" -#include "Print.hpp" -#include "SupportMaterial.hpp" -#include "Fill/FillBase.hpp" -#include "Geometry.hpp" -#include "Point.hpp" -#include "MutablePolygon.hpp" +#include "../ClipperUtils.hpp" +#include "../ExtrusionEntityCollection.hpp" +#include "../Layer.hpp" +#include "../Print.hpp" +#include "../Fill/FillBase.hpp" +#include "../Geometry.hpp" +#include "../Point.hpp" +#include "../MutablePolygon.hpp" #include "Support/SupportCommon.hpp" +#include "SupportMaterial.hpp" #include @@ -40,8 +40,8 @@ #define DEBUG #define _DEBUG #undef NDEBUG - #include "utils.hpp" - #include "SVG.hpp" + #include "../utils.hpp" + #include "../SVG.hpp" #endif #include diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/Support/SupportMaterial.hpp similarity index 97% rename from src/libslic3r/SupportMaterial.hpp rename to src/libslic3r/Support/SupportMaterial.hpp index bbb991c3e6..924ed1a052 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/Support/SupportMaterial.hpp @@ -1,12 +1,12 @@ #ifndef slic3r_SupportMaterial_hpp_ #define slic3r_SupportMaterial_hpp_ -#include "Flow.hpp" -#include "PrintConfig.hpp" -#include "Slicing.hpp" +#include "../Flow.hpp" +#include "../PrintConfig.hpp" +#include "../Slicing.hpp" -#include "Support/SupportLayer.hpp" -#include "Support/SupportParameters.hpp" +#include "SupportLayer.hpp" +#include "SupportParameters.hpp" namespace Slic3r { diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/Support/TreeModelVolumes.cpp similarity index 99% rename from src/libslic3r/TreeModelVolumes.cpp rename to src/libslic3r/Support/TreeModelVolumes.cpp index 96f61a07da..fd5437f9ed 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/Support/TreeModelVolumes.cpp @@ -9,15 +9,15 @@ #include "TreeModelVolumes.hpp" #include "TreeSupport.hpp" -#include "BuildVolume.hpp" -#include "ClipperUtils.hpp" -#include "Flow.hpp" -#include "Layer.hpp" -#include "Point.hpp" -#include "Print.hpp" -#include "PrintConfig.hpp" -#include "Utils.hpp" -#include "format.hpp" +#include "../BuildVolume.hpp" +#include "../ClipperUtils.hpp" +#include "../Flow.hpp" +#include "../Layer.hpp" +#include "../Point.hpp" +#include "../Print.hpp" +#include "../PrintConfig.hpp" +#include "../Utils.hpp" +#include "../format.hpp" #include diff --git a/src/libslic3r/TreeModelVolumes.hpp b/src/libslic3r/Support/TreeModelVolumes.hpp similarity index 99% rename from src/libslic3r/TreeModelVolumes.hpp rename to src/libslic3r/Support/TreeModelVolumes.hpp index 659baf1ff6..2b7ab5e1bc 100644 --- a/src/libslic3r/TreeModelVolumes.hpp +++ b/src/libslic3r/Support/TreeModelVolumes.hpp @@ -14,9 +14,9 @@ #include -#include "Point.hpp" -#include "Polygon.hpp" -#include "PrintConfig.hpp" +#include "../Point.hpp" +#include "../Polygon.hpp" +#include "../PrintConfig.hpp" namespace Slic3r { diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp similarity index 100% rename from src/libslic3r/TreeSupport.cpp rename to src/libslic3r/Support/TreeSupport.cpp diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp similarity index 100% rename from src/libslic3r/TreeSupport.hpp rename to src/libslic3r/Support/TreeSupport.hpp From 89f0895dd67f6ec94732ca98c4e0f69dd1645870 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 5 May 2023 14:23:09 +0200 Subject: [PATCH 64/70] WIP: Organic supports - bottom interfaces --- src/libslic3r/Support/TreeModelVolumes.cpp | 4 +++- src/libslic3r/Support/TreeModelVolumes.hpp | 4 +++- src/libslic3r/Support/TreeSupport.cpp | 18 +++++++++++++----- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Support/TreeModelVolumes.cpp b/src/libslic3r/Support/TreeModelVolumes.cpp index fd5437f9ed..5df56cd62d 100644 --- a/src/libslic3r/Support/TreeModelVolumes.cpp +++ b/src/libslic3r/Support/TreeModelVolumes.cpp @@ -79,7 +79,9 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr // this->support_interface_skip_height = // this->support_infill_angles = this->support_roof_enable = config.support_material_interface_layers.value > 0; - this->support_roof_height = config.support_material_interface_layers.value * this->layer_height; + this->support_roof_layers = this->support_roof_enable ? config.support_material_interface_layers.value : 0; + this->support_floor_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value > 0; + this->support_floor_layers = this->support_floor_enable ? config.support_material_bottom_interface_layers.value : 0; // this->minimum_roof_area = // this->support_roof_angles = this->support_roof_pattern = config.support_material_interface_pattern; diff --git a/src/libslic3r/Support/TreeModelVolumes.hpp b/src/libslic3r/Support/TreeModelVolumes.hpp index 2b7ab5e1bc..ecfa999714 100644 --- a/src/libslic3r/Support/TreeModelVolumes.hpp +++ b/src/libslic3r/Support/TreeModelVolumes.hpp @@ -94,7 +94,9 @@ struct TreeSupportMeshGroupSettings { bool support_roof_enable { false }; // Support Roof Thickness // The thickness of the support roofs. This controls the amount of dense layers at the top of the support on which the model rests. - coord_t support_roof_height { scaled(1.) }; + coord_t support_roof_layers { 2 }; + bool support_floor_enable { false }; + coord_t support_floor_layers { 2 }; // Minimum Support Roof Area // Minimum area size for the roofs of the support. Polygons which have an area smaller than this value will be printed as normal support. double minimum_roof_area { scaled(scaled(1.)) }; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index a3264990aa..d1306962e8 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1460,7 +1460,7 @@ static void generate_initial_areas( //FIXME this is a heuristic value for support enforcers to work. // + 10 * config.support_line_width; ; - const size_t num_support_roof_layers = mesh_group_settings.support_roof_enable ? (mesh_group_settings.support_roof_height + config.layer_height / 2) / config.layer_height : 0; + const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers; const bool roof_enabled = num_support_roof_layers > 0; const bool force_tip_to_roof = sqr(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area && roof_enabled; // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point @@ -4211,6 +4211,7 @@ static std::vector draw_branches( struct Slice { Polygons polygons; + Polygons bottom_interfaces; size_t num_branches{ 0 }; }; @@ -4310,6 +4311,7 @@ static std::vector draw_branches( const SlicingParameters &slicing_params = print_object.slicing_parameters(); MeshSlicingParams mesh_slicing_params; mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive; + tbb::parallel_for(tbb::blocked_range(0, trees.size(), 1), [&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { indexed_triangle_set partial_mesh; @@ -4343,6 +4345,7 @@ static std::vector draw_branches( struct BottomExtraSlice { Polygons polygons; Polygons supported; + Polygons bottom_interfaces; double area; double supported_area; }; @@ -4371,12 +4374,17 @@ static std::vector draw_branches( */ Polygons supported; double supported_area; - bottom_extra_slices.push_back({ rest_support, std::move(supported), rest_support_area, supported_area }); + bottom_extra_slices.push_back({ rest_support, std::move(supported), {}, rest_support_area, supported_area }); } // Now remove those bottom slices that are not supported at all. - while (! bottom_extra_slices.empty() && - area(intersection_clipped(bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}))) < support_area_min) - bottom_extra_slices.pop_back(); + while (! bottom_extra_slices.empty()) { + Polygons bottom_interfaces = intersection_clipped(bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {})); + if (area(bottom_interfaces) < support_area_min) + bottom_extra_slices.pop_back(); + else { + bottom_extra_slices.back().bottom_interfaces = std::move(bottom_interfaces); + } + } layer_begin -= LayerIndex(bottom_extra_slices.size()); slices.insert(slices.begin(), bottom_extra_slices.size(), {}); size_t i = 0; From ce4cf95067d0bd51824d3951f51881645ab2605b Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 5 May 2023 16:31:27 +0200 Subject: [PATCH 65/70] Fix missing includes --- src/libslic3r/Support/SupportCommon.cpp | 1 + src/libslic3r/Support/SupportLayer.hpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index f76ebaa0ed..20e8558dcc 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -1,4 +1,5 @@ #include "../ClipperUtils.hpp" +#include "../ClipperZUtils.hpp" #include "../ExtrusionEntityCollection.hpp" #include "../Layer.hpp" #include "../Print.hpp" diff --git a/src/libslic3r/Support/SupportLayer.hpp b/src/libslic3r/Support/SupportLayer.hpp index 82265881c7..155de70ce6 100644 --- a/src/libslic3r/Support/SupportLayer.hpp +++ b/src/libslic3r/Support/SupportLayer.hpp @@ -5,6 +5,8 @@ #include // for Slic3r::deque #include "../libslic3r.h" +#include "ClipperUtils.hpp" +#include "Polygon.hpp" namespace Slic3r::FFFSupport { From 1e7a3216ca87d9fdeaa5b12f813e5900a77d49bc Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 8 May 2023 09:19:31 +0200 Subject: [PATCH 66/70] WIP Organic supports intefaces: Further refactor of FDM support code - extracted interface routine to common. Implemented support for soluble interfaces & half soluble / half non-soluble interfaces. --- src/libslic3r/Support/SupportCommon.cpp | 189 +++++++++ src/libslic3r/Support/SupportCommon.hpp | 17 + src/libslic3r/Support/SupportMaterial.cpp | 169 +------- src/libslic3r/Support/SupportMaterial.hpp | 10 - src/libslic3r/Support/SupportParameters.cpp | 28 +- src/libslic3r/Support/SupportParameters.hpp | 24 ++ src/libslic3r/Support/TreeSupport.cpp | 405 ++++++++++++-------- src/libslic3r/Support/TreeSupport.hpp | 4 - 8 files changed, 507 insertions(+), 339 deletions(-) diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index f76ebaa0ed..4978b9e40c 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -3,6 +3,7 @@ #include "../Layer.hpp" #include "../Print.hpp" #include "../Fill/FillBase.hpp" +#include "../MutablePolygon.hpp" #include "../Geometry.hpp" #include "../Point.hpp" @@ -118,6 +119,194 @@ void remove_bridges_from_contacts( #endif /* SLIC3R_DEBUG */ } +// Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers. +std::pair generate_interface_layers( + const PrintObjectConfig &config, + const SupportParameters &support_params, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + // Input / output, will be merged with output. Only provided for Organic supports. + SupportGeneratorLayersPtr &top_interface_layers, + SupportGeneratorLayersPtr &top_base_interface_layers, + // Input, will be trimmed with the newly created interface layers. + SupportGeneratorLayersPtr &intermediate_layers, + SupportGeneratorLayerStorage &layer_storage) +{ + std::pair base_and_interface_layers; + + if (! intermediate_layers.empty() && support_params.has_interfaces()) { + // For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers. + BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start"; + const bool snug_supports = config.support_material_style.value != smsGrid; + SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first; + SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second; + interface_layers.assign(intermediate_layers.size(), nullptr); + if (support_params.has_base_interfaces()) + base_interface_layers.assign(intermediate_layers.size(), nullptr); + const auto smoothing_distance = support_params.support_material_interface_flow.scaled_spacing() * 1.5; + const auto minimum_island_radius = support_params.support_material_interface_flow.scaled_spacing() / support_params.interface_density; + const auto closing_distance = smoothing_distance; // scaled(config.support_material_closing_radius.value); + // Insert a new layer into base_interface_layers, if intersection with base exists. + auto insert_layer = [&layer_storage, snug_supports, closing_distance, smoothing_distance, minimum_island_radius]( + SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer, + const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* { + bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty(); + assert(! bottom.empty() || ! top.empty() || has_top_interface); + // Merge top into bottom, unite them with a safety offset. + append(bottom, std::move(top)); + // Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners). + bottom = intersection( + snug_supports ? + smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : + union_safety_offset(std::move(bottom)), + intermediate_layer.polygons); + if (has_top_interface) + // Don't trim the precomputed Organic supports top interface with base layer + // as the precomputed top interface likely expands over multiple tree tips. + bottom = union_(std::move(top_interface_layer->polygons), bottom); + if (! bottom.empty()) { + //FIXME Remove non-printable tiny islands, let them be printed using the base support. + //bottom = opening(std::move(bottom), minimum_island_radius); + if (! bottom.empty()) { + SupportGeneratorLayer &layer_new = top_interface_layer ? *top_interface_layer : layer_storage.allocate(type); + layer_new.polygons = std::move(bottom); + layer_new.print_z = intermediate_layer.print_z; + layer_new.bottom_z = intermediate_layer.bottom_z; + layer_new.height = intermediate_layer.height; + layer_new.bridging = intermediate_layer.bridging; + // Subtract the interface from the base regions. + intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons); + if (subtract) + // Trim the base interface layer with the interface layer. + layer_new.polygons = diff(std::move(layer_new.polygons), *subtract); + //FIXME filter layer_new.polygons islands by a minimum area? + // $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ]; + return &layer_new; + } + } + return nullptr; + }; + tbb::parallel_for(tbb::blocked_range(0, int(intermediate_layers.size())), + [&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer, &support_params, + snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range& range) { + // Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below + // this intermediate layer. + // Index of the first top contact layer intersecting the current intermediate layer. + auto idx_top_contact_first = -1; + // Index of the first bottom contact layer intersecting the current intermediate layer. + auto idx_bottom_contact_first = -1; + // Index of the first top interface layer intersecting the current intermediate layer. + auto idx_top_interface_first = -1; + // Index of the first top contact interface layer intersecting the current intermediate layer. + auto idx_top_base_interface_first = -1; + auto num_intermediate = int(intermediate_layers.size()); + for (int idx_intermediate_layer = range.begin(); idx_intermediate_layer < range.end(); ++ idx_intermediate_layer) { + SupportGeneratorLayer &intermediate_layer = *intermediate_layers[idx_intermediate_layer]; + Polygons polygons_top_contact_projected_interface; + Polygons polygons_top_contact_projected_base; + Polygons polygons_bottom_contact_projected_interface; + Polygons polygons_bottom_contact_projected_base; + if (support_params.num_top_interface_layers > 0) { + // Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces + coordf_t top_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + int(support_params.num_top_interface_layers) - 1)]->print_z; + coordf_t top_inteface_z = std::numeric_limits::max(); + if (support_params.num_top_base_interface_layers > 0) + // Some top base interface layers will be generated. + top_inteface_z = support_params.num_top_interface_layers_only() == 0 ? + // Only base interface layers to generate. + - std::numeric_limits::max() : + intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + int(support_params.num_top_interface_layers_only()) - 1)]->print_z; + // Move idx_top_contact_first up until above the current print_z. + idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const SupportGeneratorLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON + // Collect the top contact areas above this intermediate layer, below top_z. + for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) { + const SupportGeneratorLayer &top_contact_layer = *top_contacts[idx_top_contact]; + //FIXME maybe this adds one interface layer in excess? + if (top_contact_layer.bottom_z - EPSILON > top_z) + break; + polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, + // For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers. + // For grid supports, merging of support regions will be performed by the projection into grid. + snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons); + } + } + if (support_params.num_bottom_interface_layers > 0) { + // Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces + coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers) + 1)]->bottom_z; + coordf_t bottom_interface_z = - std::numeric_limits::max(); + if (support_params.num_bottom_base_interface_layers > 0) + // Some bottom base interface layers will be generated. + bottom_interface_z = support_params.num_bottom_interface_layers_only() == 0 ? + // Only base interface layers to generate. + std::numeric_limits::max() : + intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers_only()))]->bottom_z; + // Move idx_bottom_contact_first up until touching bottom_z. + idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const SupportGeneratorLayer *layer){ return layer->print_z >= bottom_z - EPSILON; }); + // Collect the top contact areas above this intermediate layer, below top_z. + for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) { + const SupportGeneratorLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact]; + if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z) + break; + polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons); + } + } + auto resolve_same_layer = [](SupportGeneratorLayersPtr &layers, int &idx, coordf_t print_z) -> SupportGeneratorLayer* { + if (! layers.empty()) { + idx = idx_higher_or_equal(layers, idx, [print_z](const SupportGeneratorLayer *layer) { return layer->print_z > print_z - EPSILON; }); + if (idx < int(layers.size()) && layers[idx]->print_z < print_z + EPSILON) { + SupportGeneratorLayer *l = layers[idx]; + // Remove the layer from the source container, as it will be consumed here: It will be merged + // with the newly produced interfaces. + layers[idx] = nullptr; + return l; + } + } + return nullptr; + }; + SupportGeneratorLayer *top_interface_layer = resolve_same_layer(top_interface_layers, idx_top_interface_first, intermediate_layer.print_z); + SupportGeneratorLayer *top_base_interface_layer = resolve_same_layer(top_base_interface_layers, idx_top_base_interface_first, intermediate_layer.print_z); + SupportGeneratorLayer *interface_layer = nullptr; + if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty() || + (top_interface_layer && ! top_interface_layer->polygons.empty())) { + interface_layer = insert_layer( + intermediate_layer, polygons_bottom_contact_projected_interface, std::move(polygons_top_contact_projected_interface), top_interface_layer, + nullptr, polygons_top_contact_projected_interface.empty() ? SupporLayerType::BottomInterface : SupporLayerType::TopInterface); + interface_layers[idx_intermediate_layer] = interface_layer; + } + if (! polygons_bottom_contact_projected_base.empty() || ! polygons_top_contact_projected_base.empty() || + (top_base_interface_layer && ! top_base_interface_layer->polygons.empty())) + base_interface_layers[idx_intermediate_layer] = insert_layer( + intermediate_layer, polygons_bottom_contact_projected_base, std::move(polygons_top_contact_projected_base), top_base_interface_layer, + interface_layer ? &interface_layer->polygons : nullptr, SupporLayerType::Base); + } + }); + + // Compress contact_out, remove the nullptr items. + // The parallel_for above may not have merged all the interface and base_interface layers + // generated by the Organic supports code, do it here. + auto merge_remove_nulls = [](SupportGeneratorLayersPtr &in1, SupportGeneratorLayersPtr &in2) { + size_t nonzeros = std::count_if(in1.begin(), in1.end(), [](auto *l) { return l != nullptr; }) + + std::count_if(in2.begin(), in2.end(), [](auto *l) { return l != nullptr; }); + remove_nulls(in1); + remove_nulls(in2); + if (in2.empty()) + return std::move(in1); + else if (in1.empty()) + return std::move(in2); + else { + SupportGeneratorLayersPtr out(in1.size() + in2.size(), nullptr); + std::merge(in1.begin(), in1.end(), in2.begin(), in2.end(), out.begin(), [](auto* l, auto* r) { return l->print_z < r->print_z; }); + return std::move(out); + } + }; + interface_layers = merge_remove_nulls(interface_layers, top_interface_layers); + base_interface_layers = merge_remove_nulls(base_interface_layers, top_base_interface_layers); + BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end"; + } + + return base_and_interface_layers; +} + SupportGeneratorLayersPtr generate_raft_base( const PrintObject &object, const SupportParameters &support_params, diff --git a/src/libslic3r/Support/SupportCommon.hpp b/src/libslic3r/Support/SupportCommon.hpp index 4eabce772a..19f5822fe5 100644 --- a/src/libslic3r/Support/SupportCommon.hpp +++ b/src/libslic3r/Support/SupportCommon.hpp @@ -21,6 +21,23 @@ void remove_bridges_from_contacts( float fw, Polygons &contact_polygons); +// Turn some of the base layers into base interface layers. +// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base +// extruder to improve adhesion of the soluble filament to the base. +// For Organic supports, merge top_interface_layers & top_base_interface_layers with the interfaces +// produced by this function. +std::pair generate_interface_layers( + const PrintObjectConfig &config, + const SupportParameters &support_params, + const SupportGeneratorLayersPtr &bottom_contacts, + const SupportGeneratorLayersPtr &top_contacts, + // Input / output, will be merged with output + SupportGeneratorLayersPtr &top_interface_layers, + SupportGeneratorLayersPtr &top_base_interface_layers, + // Input, will be trimmed with the newly created interface layers. + SupportGeneratorLayersPtr &intermediate_layers, + SupportGeneratorLayerStorage &layer_storage); + // Generate raft layers, also expand the 1st support layer // in case there is no raft layer to improve support adhesion. SupportGeneratorLayersPtr generate_raft_base( diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 347278911f..a21a48b9a3 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -335,14 +335,16 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Propagate top / bottom contact layers to generate interface layers // and base interface layers (for soluble interface / non souble base only) - auto [interface_layers, base_interface_layers] = this->generate_interface_layers(bottom_contacts, top_contacts, intermediate_layers, layer_storage); + SupportGeneratorLayersPtr empty_layers; + auto [interface_layers, base_interface_layers] = FFFSupport::generate_interface_layers( + *m_object_config, m_support_params, bottom_contacts, top_contacts, empty_layers, empty_layers, intermediate_layers, layer_storage); BOOST_LOG_TRIVIAL(info) << "Support generator - Creating raft"; // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled. // There is also a 1st intermediate layer containing bases of support columns. // Inflate the bases of the support columns and create the raft base under the object. - SupportGeneratorLayersPtr raft_layers = generate_raft_base(object, m_support_params, m_slicing_params, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); + SupportGeneratorLayersPtr raft_layers = FFFSupport::generate_raft_base(object, m_support_params, m_slicing_params, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); #ifdef SLIC3R_DEBUG for (const SupportGeneratorLayer *l : interface_layers) @@ -2514,169 +2516,6 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end"; } -// Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers. -std::pair PrintObjectSupportMaterial::generate_interface_layers( - const SupportGeneratorLayersPtr &bottom_contacts, - const SupportGeneratorLayersPtr &top_contacts, - SupportGeneratorLayersPtr &intermediate_layers, - SupportGeneratorLayerStorage &layer_storage) const -{ -// my $area_threshold = $self->interface_flow->scaled_spacing ** 2; - - std::pair base_and_interface_layers; - SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first; - SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second; - - // distinguish between interface and base interface layers - // Contact layer is considered an interface layer, therefore run the following block only if support_material_interface_layers > 1. - // Contact layer needs a base_interface layer, therefore run the following block if support_material_interface_layers > 0, has soluble support and extruders are different. - bool soluble_interface_non_soluble_base = - // Zero z-gap between the overhangs and the support interface. - m_slicing_params.soluble_interface && - // Interface extruder soluble. - m_object_config->support_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) && - // Base extruder: Either "print with active extruder" not soluble. - (m_object_config->support_material_extruder.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_material_extruder.value - 1)); - bool snug_supports = m_object_config->support_material_style.value != smsGrid; - int num_interface_layers_top = m_object_config->support_material_interface_layers; - int num_interface_layers_bottom = m_object_config->support_material_bottom_interface_layers; - if (num_interface_layers_bottom < 0) - num_interface_layers_bottom = num_interface_layers_top; - int num_base_interface_layers_top = soluble_interface_non_soluble_base ? std::min(num_interface_layers_top / 2, 2) : 0; - int num_base_interface_layers_bottom = soluble_interface_non_soluble_base ? std::min(num_interface_layers_bottom / 2, 2) : 0; - - if (! intermediate_layers.empty() && (num_interface_layers_top > 1 || num_interface_layers_bottom > 1)) { - // For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers. - BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start"; - // Since the intermediate layer index starts at zero the number of interface layer needs to be reduced by 1. - -- num_interface_layers_top; - -- num_interface_layers_bottom; - int num_interface_layers_only_top = num_interface_layers_top - num_base_interface_layers_top; - int num_interface_layers_only_bottom = num_interface_layers_bottom - num_base_interface_layers_bottom; - interface_layers.assign(intermediate_layers.size(), nullptr); - if (num_base_interface_layers_top || num_base_interface_layers_bottom) - base_interface_layers.assign(intermediate_layers.size(), nullptr); - auto smoothing_distance = m_support_params.support_material_interface_flow.scaled_spacing() * 1.5; - auto minimum_island_radius = m_support_params.support_material_interface_flow.scaled_spacing() / m_support_params.interface_density; - auto closing_distance = smoothing_distance; // scaled(m_object_config->support_material_closing_radius.value); - // Insert a new layer into base_interface_layers, if intersection with base exists. - auto insert_layer = [&layer_storage, snug_supports, closing_distance, smoothing_distance, minimum_island_radius]( - SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* { - assert(! bottom.empty() || ! top.empty()); - // Merge top into bottom, unite them with a safety offset. - append(bottom, std::move(top)); - // Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners). - bottom = intersection( - snug_supports ? - smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : - union_safety_offset(std::move(bottom)), - intermediate_layer.polygons); - if (! bottom.empty()) { - //FIXME Remove non-printable tiny islands, let them be printed using the base support. - //bottom = opening(std::move(bottom), minimum_island_radius); - if (! bottom.empty()) { - SupportGeneratorLayer &layer_new = layer_storage.allocate(type); - layer_new.polygons = std::move(bottom); - layer_new.print_z = intermediate_layer.print_z; - layer_new.bottom_z = intermediate_layer.bottom_z; - layer_new.height = intermediate_layer.height; - layer_new.bridging = intermediate_layer.bridging; - // Subtract the interface from the base regions. - intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons); - if (subtract) - // Trim the base interface layer with the interface layer. - layer_new.polygons = diff(std::move(layer_new.polygons), *subtract); - //FIXME filter layer_new.polygons islands by a minimum area? - // $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ]; - return &layer_new; - } - } - return nullptr; - }; - tbb::parallel_for(tbb::blocked_range(0, int(intermediate_layers.size())), - [&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer, - num_interface_layers_top, num_interface_layers_bottom, num_base_interface_layers_top, num_base_interface_layers_bottom, num_interface_layers_only_top, num_interface_layers_only_bottom, - snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range& range) { - // Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below - // this intermediate layer. - // Index of the first top contact layer intersecting the current intermediate layer. - auto idx_top_contact_first = -1; - // Index of the first bottom contact layer intersecting the current intermediate layer. - auto idx_bottom_contact_first = -1; - auto num_intermediate = int(intermediate_layers.size()); - for (int idx_intermediate_layer = range.begin(); idx_intermediate_layer < range.end(); ++ idx_intermediate_layer) { - SupportGeneratorLayer &intermediate_layer = *intermediate_layers[idx_intermediate_layer]; - Polygons polygons_top_contact_projected_interface; - Polygons polygons_top_contact_projected_base; - Polygons polygons_bottom_contact_projected_interface; - Polygons polygons_bottom_contact_projected_base; - if (num_interface_layers_top > 0) { - // Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces - coordf_t top_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_top - 1)]->print_z; - coordf_t top_inteface_z = std::numeric_limits::max(); - if (num_base_interface_layers_top > 0) - // Some top base interface layers will be generated. - top_inteface_z = num_interface_layers_only_top == 0 ? - // Only base interface layers to generate. - - std::numeric_limits::max() : - intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_only_top - 1)]->print_z; - // Move idx_top_contact_first up until above the current print_z. - idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const SupportGeneratorLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON - // Collect the top contact areas above this intermediate layer, below top_z. - for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) { - const SupportGeneratorLayer &top_contact_layer = *top_contacts[idx_top_contact]; - //FIXME maybe this adds one interface layer in excess? - if (top_contact_layer.bottom_z - EPSILON > top_z) - break; - polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, - // For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers. - // For grid supports, merging of support regions will be performed by the projection into grid. - snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons); - } - } - if (num_interface_layers_bottom > 0) { - // Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces - coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_bottom + 1)]->bottom_z; - coordf_t bottom_interface_z = - std::numeric_limits::max(); - if (num_base_interface_layers_bottom > 0) - // Some bottom base interface layers will be generated. - bottom_interface_z = num_interface_layers_only_bottom == 0 ? - // Only base interface layers to generate. - std::numeric_limits::max() : - intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only_bottom)]->bottom_z; - // Move idx_bottom_contact_first up until touching bottom_z. - idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const SupportGeneratorLayer *layer){ return layer->print_z >= bottom_z - EPSILON; }); - // Collect the top contact areas above this intermediate layer, below top_z. - for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) { - const SupportGeneratorLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact]; - if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z) - break; - polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons); - } - } - SupportGeneratorLayer *interface_layer = nullptr; - if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty()) { - interface_layer = insert_layer( - intermediate_layer, polygons_bottom_contact_projected_interface, std::move(polygons_top_contact_projected_interface), nullptr, - polygons_top_contact_projected_interface.empty() ? SupporLayerType::BottomInterface : SupporLayerType::TopInterface); - interface_layers[idx_intermediate_layer] = interface_layer; - } - if (! polygons_bottom_contact_projected_base.empty() || ! polygons_top_contact_projected_base.empty()) - base_interface_layers[idx_intermediate_layer] = insert_layer( - intermediate_layer, polygons_bottom_contact_projected_base, std::move(polygons_top_contact_projected_base), - interface_layer ? &interface_layer->polygons : nullptr, SupporLayerType::Base); - } - }); - - // Compress contact_out, remove the nullptr items. - remove_nulls(interface_layers); - remove_nulls(base_interface_layers); - BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end"; - } - - return base_and_interface_layers; -} - /* void PrintObjectSupportMaterial::clip_by_pillars( const PrintObject &object, diff --git a/src/libslic3r/Support/SupportMaterial.hpp b/src/libslic3r/Support/SupportMaterial.hpp index 924ed1a052..4f1768fb1e 100644 --- a/src/libslic3r/Support/SupportMaterial.hpp +++ b/src/libslic3r/Support/SupportMaterial.hpp @@ -72,16 +72,6 @@ private: SupportGeneratorLayersPtr &intermediate_layers, const std::vector &layer_support_areas) const; - // Turn some of the base layers into base interface layers. - // For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base - // extruder to improve adhesion of the soluble filament to the base. - std::pair generate_interface_layers( - const SupportGeneratorLayersPtr &bottom_contacts, - const SupportGeneratorLayersPtr &top_contacts, - SupportGeneratorLayersPtr &intermediate_layers, - SupportGeneratorLayerStorage &layer_storage) const; - - // Trim support layers by an object to leave a defined gap between // the support volume and the object. void trim_support_layers_by_object( diff --git a/src/libslic3r/Support/SupportParameters.cpp b/src/libslic3r/Support/SupportParameters.cpp index 1c7f860b88..531e8dcafc 100644 --- a/src/libslic3r/Support/SupportParameters.cpp +++ b/src/libslic3r/Support/SupportParameters.cpp @@ -11,6 +11,33 @@ SupportParameters::SupportParameters(const PrintObject &object) const PrintObjectConfig &object_config = object.config(); const SlicingParameters &slicing_params = object.slicing_parameters(); + this->soluble_interface = slicing_params.soluble_interface; + this->soluble_interface_non_soluble_base = + // Zero z-gap between the overhangs and the support interface. + slicing_params.soluble_interface && + // Interface extruder soluble. + object_config.support_material_interface_extruder.value > 0 && print_config.filament_soluble.get_at(object_config.support_material_interface_extruder.value - 1) && + // Base extruder: Either "print with active extruder" not soluble. + (object_config.support_material_extruder.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_material_extruder.value - 1)); + + { + int num_top_interface_layers = std::max(0, object_config.support_material_interface_layers.value); + int num_bottom_interface_layers = object_config.support_material_bottom_interface_layers < 0 ? + num_top_interface_layers : object_config.support_material_bottom_interface_layers; + this->has_top_contacts = num_top_interface_layers > 0; + this->has_bottom_contacts = num_bottom_interface_layers > 0; + this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0; + this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0; + if (this->soluble_interface_non_soluble_base) { + // Try to support soluble dense interfaces with non-soluble dense interfaces. + this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2)); + this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2)); + } else { + this->num_top_base_interface_layers = 0; + this->num_bottom_base_interface_layers = 0; + } + } + this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height)); this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height)); @@ -54,7 +81,6 @@ SupportParameters::SupportParameters(const PrintObject &object) this->can_merge_support_regions = true; } - double interface_spacing = object_config.support_material_interface_spacing.value + this->support_material_interface_flow.spacing(); this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing); double raft_interface_spacing = object_config.support_material_interface_spacing.value + this->raft_interface_flow.spacing(); diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 904e8ffe29..be38e9650a 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -14,6 +14,30 @@ namespace FFFSupport { struct SupportParameters { SupportParameters(const PrintObject &object); + // Both top / bottom contacts and interfaces are soluble. + bool soluble_interface; + // Support contact & interface are soluble, but support base is non-soluble. + bool soluble_interface_non_soluble_base; + + // Is there at least a top contact layer extruded above support base? + bool has_top_contacts; + // Is there at least a bottom contact layer extruded below support base? + bool has_bottom_contacts; + // Number of top interface layers without counting the contact layer. + size_t num_top_interface_layers; + // Number of bottom interface layers without counting the contact layer. + size_t num_bottom_interface_layers; + // Number of top base interface layers. Zero if not soluble_interface_non_soluble_base. + size_t num_top_base_interface_layers; + // Number of bottom base interface layers. Zero if not soluble_interface_non_soluble_base. + size_t num_bottom_base_interface_layers; + + bool has_contacts() const { return this->has_top_contacts || this->has_bottom_contacts; } + bool has_interfaces() const { return this->num_top_interface_layers + this->num_bottom_interface_layers > 0; } + bool has_base_interfaces() const { return this->num_top_base_interface_layers + this->num_bottom_base_interface_layers > 0; } + size_t num_top_interface_layers_only() const { return this->num_top_interface_layers - this->num_top_base_interface_layers; } + size_t num_bottom_interface_layers_only() const { return this->num_bottom_interface_layers - this->num_bottom_base_interface_layers; } + // Flow at the 1st print layer. Flow first_layer_flow; // Flow at the support base (neither top, nor bottom interface). diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index d1306962e8..dfeb3fdf94 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -87,7 +87,6 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)), z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))), z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))), - performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, layer_height)), // support_infill_angles(mesh_group_settings.support_infill_angles), support_roof_angles(mesh_group_settings.support_roof_angles), roof_pattern(mesh_group_settings.support_roof_pattern), @@ -1077,71 +1076,53 @@ void finalize_raft_contact( } } +// Used by generate_initial_areas() in parallel by multiple layers. class InterfacePlacer { public: - InterfacePlacer(const SlicingParameters &slicing_parameters, const TreeModelVolumes &volumes, const TreeSupportSettings &config, bool force_tip_to_roof, size_t num_support_layers, - std::vector &move_bounds, SupportGeneratorLayerStorage &layer_storage, SupportGeneratorLayersPtr &top_contacts) : - slicing_parameters(slicing_parameters), volumes(volumes), config(config), force_tip_to_roof(force_tip_to_roof), - move_bounds(move_bounds), layer_storage(layer_storage), top_contacts(top_contacts) { + InterfacePlacer( + const SlicingParameters &slicing_parameters, + const SupportParameters &support_parameters, + const TreeModelVolumes &volumes, const TreeSupportSettings &config, + bool force_tip_to_roof, size_t num_support_layers, + std::vector &move_bounds, + SupportGeneratorLayerStorage &layer_storage, + SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &top_interfaces, SupportGeneratorLayersPtr &top_base_interfaces) + : + slicing_parameters(slicing_parameters), support_parameters(support_parameters), volumes(volumes), config(config), force_tip_to_roof(force_tip_to_roof), + move_bounds(move_bounds), + layer_storage(layer_storage), top_contacts(top_contacts), top_interfaces(top_interfaces), top_base_interfaces(top_base_interfaces) { m_already_inserted.assign(num_support_layers, {}); this->min_xy_dist = config.xy_distance > config.xy_min_distance; } const SlicingParameters &slicing_parameters; + const SupportParameters &support_parameters; const TreeModelVolumes &volumes; const TreeSupportSettings &config; - bool force_tip_to_roof; + // Radius of the tree tip is large enough to be covered by an interface. + const bool force_tip_to_roof; bool min_xy_dist; - // Outputs - std::vector &move_bounds; - SupportGeneratorLayerStorage &layer_storage; - SupportGeneratorLayersPtr &top_contacts; - -private: - // Temps - static constexpr const auto m_base_radius = scaled(0.01); - const Polygon m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) }; - - // Mutexes, guards - std::mutex m_mutex_movebounds; - std::mutex m_mutex_layer_storage; - std::vector> m_already_inserted; - public: - void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx) - { - SupportGeneratorLayer*& l = top_contacts[insert_layer_idx]; - if (l == nullptr) - l = &layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, insert_layer_idx); - // will be unioned in finalize_interface_and_support_areas() - append(l->polygons, std::move(new_roofs)); - } - - void add_roof(Polygons &&new_roofs, const size_t insert_layer_idx) - { - std::lock_guard lock(m_mutex_layer_storage); - add_roof_unguarded(std::move(new_roofs), insert_layer_idx); - } - - void add_roofs(std::vector &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof) + // called by sample_overhang_area() + // Insert the contact layer and some of the inteface and base interface layers below. + void add_roofs(std::vector &&new_roofs, const size_t insert_layer_idx) { if (! new_roofs.empty()) { std::lock_guard lock(m_mutex_layer_storage); - for (size_t idx = 0; idx < dtt_roof; ++ idx) + for (size_t idx = 0; idx < new_roofs.size(); ++ idx) if (! new_roofs[idx].empty()) - add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx); + add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx, idx); } } - void add_roof_build_plate(Polygons &&overhang_areas) + // called by sample_overhang_area() + void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof) { std::lock_guard lock(m_mutex_layer_storage); - SupportGeneratorLayer*& l = top_contacts[0]; - if (l == nullptr) - l = &layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, 0); - append(l->polygons, std::move(overhang_areas)); + this->add_roof_unguarded(std::move(overhang_areas), 0, dtt_roof); } + // called by sample_overhang_area() void add_points_along_lines( // Insert points (tree tips or top contact interfaces) along these lines. LineInformations lines, @@ -1150,7 +1131,7 @@ public: // Insert this number of interface layers. size_t roof_tip_layers, // True if an interface is already generated above these lines. - bool supports_roof, + size_t supports_roof_layers, // The element tries to not move until this dtt is reached. size_t dont_move_until) { @@ -1203,7 +1184,7 @@ public: roof_circle.translate(p.first); new_roofs.emplace_back(std::move(roof_circle)); } - this->add_roof(std::move(new_roofs), this_layer_idx); + this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers); } for (const LineInformation &line : lines) { @@ -1215,11 +1196,33 @@ public: // don't move until dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, // supports roof - dtt_roof_tip > 0 || supports_roof, + dtt_roof_tip + supports_roof_layers > 0, disable_ovalistation); } } +private: + void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof) + { + SupportGeneratorLayersPtr &layers = + dtt_roof == 0 ? this->top_contacts : + dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces; + SupportGeneratorLayer*& l = layers[insert_layer_idx]; + if (l == nullptr) + l = &layer_allocate_unguarded(layer_storage, dtt_roof == 0 ? SupporLayerType::TopContact : SupporLayerType::TopInterface, + slicing_parameters, config, insert_layer_idx); + // will be unioned in finalize_interface_and_support_areas() + append(l->polygons, std::move(new_roofs)); + } + + // called by this->add_points_along_lines() + void add_roof(Polygons &&new_roof, const size_t insert_layer_idx, const size_t dtt_tip) + { + std::lock_guard lock(m_mutex_layer_storage); + add_roof_unguarded(std::move(new_roof), insert_layer_idx, dtt_tip); + } + + // called by this->add_points_along_lines() void add_point_as_influence_area(std::pair p, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation) { bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE; @@ -1233,8 +1236,8 @@ public: Polygons circle{ m_base_circle }; circle.front().translate(p.first); { - std::lock_guard critical_section_movebounds(m_mutex_movebounds); Point hash_pos = p.first / ((config.min_radius + 1) / 10); + std::lock_guard critical_section_movebounds(m_mutex_movebounds); if (!m_already_inserted[insert_layer].count(hash_pos)) { // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing m_already_inserted[insert_layer].emplace(hash_pos); @@ -1261,9 +1264,26 @@ public: move_bounds[insert_layer].emplace_back(state, std::move(circle)); } } - }; + } + + // Outputs + std::vector &move_bounds; + SupportGeneratorLayerStorage &layer_storage; + SupportGeneratorLayersPtr &top_contacts; + SupportGeneratorLayersPtr &top_interfaces; + SupportGeneratorLayersPtr &top_base_interfaces; + + // Temps + static constexpr const auto m_base_radius = scaled(0.01); + const Polygon m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) }; + + // Mutexes, guards + std::mutex m_mutex_movebounds; + std::mutex m_mutex_layer_storage; + std::vector> m_already_inserted; }; +// Called by generate_initial_areas(), used in parallel by multiple layers. // Produce // 1) Maximum num_support_roof_layers roof (top interface & contact) layers. // 2) Tree tips supporting either the roof layers or the object itself. @@ -1280,13 +1300,12 @@ void sample_overhang_area( const bool large_horizontal_roof, // Index of the top suport layer generated by this function. const size_t layer_idx, - // Number of roof (contact, interface) layers between the overhang and tree tips. + // Maximum number of roof (contact, interface) layers between the overhang and tree tips to be generated. const size_t num_support_roof_layers, // const coord_t connect_length, // Configuration classes const TreeSupportMeshGroupSettings &mesh_group_settings, - const SupportParameters &support_params, // Configuration & Output InterfacePlacer &interface_placer) { @@ -1297,11 +1316,12 @@ void sample_overhang_area( // as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from // a decreasing roof, as there is no guarantee that a line will be above these points. Implementing a separate roof support behavior // for each pattern harms maintainability as it very well could be >100 LOC - auto generate_roof_lines = [&support_params, &mesh_group_settings](const Polygons &area, LayerIndex layer_idx) -> Polylines { - return generate_support_infill_lines(area, support_params, true, layer_idx, mesh_group_settings.support_roof_line_distance); + auto generate_roof_lines = [&interface_placer, &mesh_group_settings](const Polygons &area, LayerIndex layer_idx) -> Polylines { + return generate_support_infill_lines(area, interface_placer.support_parameters, true, layer_idx, mesh_group_settings.support_roof_line_distance); }; LineInformations overhang_lines; + // Track how many top contact / interface layers were already generated. size_t dtt_roof = 0; size_t layer_generation_dtt = 0; @@ -1325,9 +1345,9 @@ void sample_overhang_area( } Polygons overhang_area_next = diff(overhang_area, forbidden_next); if (area(overhang_area_next) < mesh_group_settings.minimum_roof_area) { - // next layer down the roof area would be to small so we have to insert our roof support here. Also convert squaremicrons to squaremilimeter - if (dtt_roof != 0) { - size_t dtt_before = dtt_roof > 0 ? dtt_roof - 1 : 0; + // Next layer down the roof area would be to small so we have to insert our roof support here. + if (dtt_roof > 0) { + size_t dtt_before = dtt_roof - 1; // Produce support head points supporting an interface layer: First produce the interface lines, then sample them. overhang_lines = split_lines( convert_lines_to_internal(interface_placer.volumes, interface_placer.config, @@ -1354,7 +1374,8 @@ void sample_overhang_area( break; } } - interface_placer.add_roofs(std::move(added_roofs), layer_idx, dtt_roof); + added_roofs.erase(added_roofs.begin() + dtt_roof, added_roofs.end()); + interface_placer.add_roofs(std::move(added_roofs), layer_idx); } if (overhang_lines.empty()) { @@ -1364,7 +1385,7 @@ void sample_overhang_area( bool supports_roof = dtt_roof > 0; bool continuous_tips = ! supports_roof && large_horizontal_roof; Polylines polylines = ensure_maximum_distance_polyline( - generate_support_infill_lines(overhang_area, support_params, supports_roof, layer_idx - layer_generation_dtt, + generate_support_infill_lines(overhang_area, interface_placer.support_parameters, supports_roof, layer_idx - layer_generation_dtt, supports_roof ? mesh_group_settings.support_roof_line_distance : mesh_group_settings.support_tree_branch_distance), continuous_tips ? interface_placer.config.min_radius / 2 : connect_length, 1); size_t point_count = 0; @@ -1388,9 +1409,10 @@ void sample_overhang_area( overhang_lines = convert_lines_to_internal(interface_placer.volumes, interface_placer.config, polylines, layer_idx - dtt_roof); } + assert(dtt_roof <= layer_idx); if (int(dtt_roof) >= layer_idx && large_horizontal_roof) - // reached buildplate - interface_placer.add_roof_build_plate(std::move(overhang_area)); + // Reached buildplate when generating contact, interface and base interface layers. + interface_placer.add_roof_build_plate(std::move(overhang_area), dtt_roof); else { // normal trees have to be generated const bool roof_enabled = num_support_roof_layers > 0; @@ -1401,8 +1423,8 @@ void sample_overhang_area( layer_idx - dtt_roof, // Remaining roof tip layers. interface_placer.force_tip_to_roof ? num_support_roof_layers - dtt_roof : 0, - // Supports roof already? - dtt_roof > 0, + // Supports roof already? How many roof layers were already produced above these tips? + dtt_roof, // Don't move until the following distance to top is reached. roof_enabled ? num_support_roof_layers - dtt_roof : 0); } @@ -1424,7 +1446,8 @@ static void generate_initial_areas( const std::vector &overhangs, std::vector &move_bounds, SupportGeneratorLayersPtr &top_contacts, - SupportGeneratorLayersPtr &top_interface_layers, + SupportGeneratorLayersPtr &top_interfaces, + SupportGeneratorLayersPtr &top_base_interfaces, SupportGeneratorLayerStorage &layer_storage, std::function throw_on_cancel) { @@ -1462,7 +1485,7 @@ static void generate_initial_areas( ; const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers; const bool roof_enabled = num_support_roof_layers > 0; - const bool force_tip_to_roof = sqr(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area && roof_enabled; + const bool force_tip_to_roof = roof_enabled && sqr(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area; // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point // may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang // does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. @@ -1496,9 +1519,9 @@ static void generate_initial_areas( raw_overhangs.push_back({ layer_idx, &overhangs[overhang_idx] }); } - InterfacePlacer interface_placer{ print_object.slicing_parameters(), volumes, config, force_tip_to_roof, num_support_layers, + InterfacePlacer interface_placer{ print_object.slicing_parameters(), support_params, volumes, config, force_tip_to_roof, num_support_layers, // Outputs - move_bounds, layer_storage, top_contacts }; + move_bounds, layer_storage, top_contacts, top_interfaces, top_base_interfaces }; tbb::parallel_for(tbb::blocked_range(0, raw_overhangs.size()), [&volumes, &config, &raw_overhangs, &mesh_group_settings, &support_params, @@ -1613,7 +1636,7 @@ static void generate_initial_areas( //check_self_intersections(overhang_regular, "overhang_regular3"); for (ExPolygon &roof_part : union_ex(overhang_roofs)) { sample_overhang_area(to_polygons(std::move(roof_part)), true, layer_idx, num_support_roof_layers, connect_length, - mesh_group_settings, support_params, interface_placer); + mesh_group_settings, interface_placer); throw_on_cancel(); } } @@ -1623,9 +1646,8 @@ static void generate_initial_areas( remove_small(overhang_regular, mesh_group_settings.minimum_support_area); for (ExPolygon &support_part : union_ex(overhang_regular)) { sample_overhang_area(to_polygons(std::move(support_part)), - // Don't false, layer_idx, num_support_roof_layers, connect_length, - mesh_group_settings, support_params, interface_placer); + mesh_group_settings, interface_placer); throw_on_cancel(); } } @@ -3392,7 +3414,7 @@ static void finalize_interface_and_support_areas( //FIXME subtract the wipe tower append(floor_layer, intersection(layer_outset, overhangs[sample_layer])); if (layers_below < config.support_bottom_layers) - layers_below = std::min(layers_below + config.performance_interface_skip_layers, config.support_bottom_layers); + layers_below = std::min(layers_below + 1, config.support_bottom_layers); else break; } @@ -4139,11 +4161,20 @@ static void organic_smooth_branches_avoid_collisions( #endif // TREE_SUPPORT_ORGANIC_NUDGE_NEW // Organic specific: Smooth branches and produce one cummulative mesh to be sliced. -static std::vector draw_branches( +static void draw_branches( PrintObject &print_object, TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector &move_bounds, + + // I/O: + SupportGeneratorLayersPtr &bottom_contacts, + SupportGeneratorLayersPtr &top_contacts, + + // Output: + SupportGeneratorLayersPtr &intermediate_layers, + SupportGeneratorLayerStorage &layer_storage, + std::function throw_on_cancel) { // All SupportElements are put into a layer independent storage to improve parallelization. @@ -4211,7 +4242,7 @@ static std::vector draw_branches( struct Slice { Polygons polygons; - Polygons bottom_interfaces; + Polygons bottom_contacts; size_t num_branches{ 0 }; }; @@ -4316,6 +4347,7 @@ static std::vector draw_branches( [&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { indexed_triangle_set partial_mesh; std::vector slice_z; + std::vector bottom_contacts; for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) { Tree &tree = trees[tree_id]; for (const Branch &branch : tree.branches) { @@ -4335,63 +4367,71 @@ static std::vector draw_branches( slice_z.emplace_back(float(0.5 * (bottom_z + print_z))); } std::vector slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel); + bottom_contacts.clear(); //FIXME parallelize? for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++ i) slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); //FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist); size_t num_empty = 0; - if (layer_begin > 0 && branch.has_root && ! branch.path.front()->state.to_model_gracious && ! slices.front().empty()) { - // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something. - struct BottomExtraSlice { - Polygons polygons; - Polygons supported; - Polygons bottom_interfaces; - double area; - double supported_area; - }; - std::vector bottom_extra_slices; - Polygons rest_support; - coord_t bottom_radius = config.getRadius(branch.path.front()->state); - // Don't propagate further than 1.5 * bottom radius. - //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height; - LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height; - LayerIndex layer_bottommost = std::max(0, layer_begin - layers_propagate_max); - // Only propagate until the rest area is smaller than this threshold. - double support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius)); - // Only propagate until the rest area is smaller than this threshold. - double support_area_min = 0.1 * M_PI * sqr(double(config.min_radius)); - for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) { - rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false)); - double rest_support_area = area(rest_support); - if (rest_support_area < support_area_stop) - // Don't propagate a fraction of the tree contact surface. - break; - // Measure how much the rest_support is actually supported. - /* - Polygons supported = intersection_clipped(rest_support, volumes.getPlaceableAreas(0, layer_idx, []{})); - double supported_area = area(supported); - printf("Supported area: %d, %lf\n", layer_idx, supported_area); - */ - Polygons supported; - double supported_area; - bottom_extra_slices.push_back({ rest_support, std::move(supported), {}, rest_support_area, supported_area }); - } - // Now remove those bottom slices that are not supported at all. - while (! bottom_extra_slices.empty()) { - Polygons bottom_interfaces = intersection_clipped(bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {})); - if (area(bottom_interfaces) < support_area_min) - bottom_extra_slices.pop_back(); - else { - bottom_extra_slices.back().bottom_interfaces = std::move(bottom_interfaces); + if (slices.front().empty()) { + // Some of the initial layers are empty. + num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); + } else { + if (branch.has_root) { + if (branch.path.front()->state.to_model_gracious) { + if (config.settings.support_floor_layers > 0) + //FIXME one may just take the whole tree slice as bottom interface. + bottom_contacts.emplace_back(intersection_clipped(slices.front(), volumes.getPlaceableAreas(0, layer_begin, [] {}))); + } else if (layer_begin > 0) { + // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something. + struct BottomExtraSlice { + Polygons polygons; + double area; + }; + std::vector bottom_extra_slices; + Polygons rest_support; + coord_t bottom_radius = config.getRadius(branch.path.front()->state); + // Don't propagate further than 1.5 * bottom radius. + //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height; + LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height; + LayerIndex layer_bottommost = std::max(0, layer_begin - layers_propagate_max); + // Only propagate until the rest area is smaller than this threshold. + double support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius)); + // Only propagate until the rest area is smaller than this threshold. + double support_area_min = 0.1 * M_PI * sqr(double(config.min_radius)); + for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) { + rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false)); + double rest_support_area = area(rest_support); + if (rest_support_area < support_area_stop) + // Don't propagate a fraction of the tree contact surface. + break; + bottom_extra_slices.push_back({ rest_support, rest_support_area }); + } + // Now remove those bottom slices that are not supported at all. + while (! bottom_extra_slices.empty()) { + Polygons this_bottom_contacts = intersection_clipped( + bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {})); + if (area(this_bottom_contacts) < support_area_min) + bottom_extra_slices.pop_back(); + else if (config.settings.support_floor_layers > 0) + bottom_contacts.emplace_back(std::move(this_bottom_contacts)); + } + if (config.settings.support_floor_layers > 0) + for (int i = int(bottom_extra_slices.size()) - 2; i >= 0; -- i) + bottom_contacts.emplace_back( + intersection_clipped(bottom_extra_slices[i].polygons, volumes.getPlaceableAreas(0, layer_begin - i - 1, [] {}))); + layer_begin -= LayerIndex(bottom_extra_slices.size()); + slices.insert(slices.begin(), bottom_extra_slices.size(), {}); + auto it_dst = slices.begin(); + for (auto it_src = bottom_extra_slices.rbegin(); it_src != bottom_extra_slices.rend(); ++ it_src) + *it_dst ++ = std::move(it_src->polygons); } } - layer_begin -= LayerIndex(bottom_extra_slices.size()); - slices.insert(slices.begin(), bottom_extra_slices.size(), {}); - size_t i = 0; - for (auto it = bottom_extra_slices.rbegin(); it != bottom_extra_slices.rend(); ++it, ++i) - slices[i] = std::move(it->polygons); - } else - num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); + if (branch.has_tip) { + // Add top slices to top contacts / interfaces / base interfaces. + //slices; + } + } layer_begin += LayerIndex(num_empty); while (! slices.empty() && slices.back().empty()) { @@ -4414,14 +4454,21 @@ static std::vector draw_branches( tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {}); tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {}); layer_begin -= LayerIndex(num_empty); - for (LayerIndex i = layer_begin; i != layer_end; ++ i) - if (Polygons &src = slices[i - layer_begin]; ! src.empty()) { + for (LayerIndex i = layer_begin; i != layer_end; ++ i) { + int j = i - layer_begin; + if (Polygons &src = slices[j]; ! src.empty()) { Slice &dst = tree.slices[i - new_begin]; - if (++ dst.num_branches > 1) + if (++ dst.num_branches > 1) { append(dst.polygons, std::move(src)); - else + if (j < bottom_contacts.size()) + append(dst.bottom_contacts, std::move(bottom_contacts[j])); + } else { dst.polygons = std::move(std::move(src)); + if (j < bottom_contacts.size()) + dst.bottom_contacts = std::move(bottom_contacts[j]); + } } + } tree.first_layer_id = new_begin; } } @@ -4434,7 +4481,8 @@ static std::vector draw_branches( Tree &tree = trees[tree_id]; for (Slice &slice : tree.slices) if (slice.num_branches > 1) { - slice.polygons = union_(slice.polygons); + slice.polygons = union_(slice.polygons); + slice.bottom_contacts = union_(slice.bottom_contacts); slice.num_branches = 1; } throw_on_cancel(); @@ -4452,25 +4500,55 @@ static std::vector draw_branches( for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i) if (Slice &src = tree.slices[i - tree.first_layer_id]; ! src.polygons.empty()) { Slice &dst = slices[i]; - if (++ dst.num_branches > 1) - append(dst.polygons, std::move(src.polygons)); - else - dst.polygons = std::move(src.polygons); + if (++ dst.num_branches > 1) { + append(dst.polygons, std::move(src.polygons)); + append(dst.bottom_contacts, std::move(src.bottom_contacts)); + } else { + dst.polygons = std::move(src.polygons); + dst.bottom_contacts = std::move(src.bottom_contacts); + } } } - std::vector support_layer_storage(move_bounds.size()); tbb::parallel_for(tbb::blocked_range(0, std::min(move_bounds.size(), slices.size()), 1), - [&slices, &support_layer_storage, &throw_on_cancel](const tbb::blocked_range &range) { - for (size_t slice_id = range.begin(); slice_id < range.end(); ++ slice_id) { - Slice &slice = slices[slice_id]; - support_layer_storage[slice_id] = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons); + [&print_object, &config, &slices, &bottom_contacts, &top_contacts, &intermediate_layers, &layer_storage, &throw_on_cancel](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + Slice &slice = slices[layer_idx]; + assert(intermediate_layers[layer_idx] == nullptr); + Polygons base_layer_polygons = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons); + Polygons bottom_contact_polygons = slice.num_branches > 1 ? union_(slice.bottom_contacts) : std::move(slice.bottom_contacts); + + if (! base_layer_polygons.empty()) { + // Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned. + base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50); + //smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : + // simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution. + base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled(0.03), double(config.resolution)), polygons_strictly_simple); + } + + // Subtract top contact layer polygons from support base. + SupportGeneratorLayer *top_contact_layer = top_contacts[layer_idx]; + if (top_contact_layer && ! top_contact_layer->polygons.empty() && ! base_layer_polygons.empty()) { + base_layer_polygons = diff(base_layer_polygons, top_contact_layer->polygons); + if (! bottom_contact_polygons.empty()) + //FIXME it may be better to clip bottom contacts with top contacts first after they are propagated to produce interface layers. + bottom_contact_polygons = diff(bottom_contact_polygons, top_contact_layer->polygons); + } + if (! bottom_contact_polygons.empty()) { + base_layer_polygons = diff(base_layer_polygons, bottom_contact_polygons); + SupportGeneratorLayer *bottom_contact_layer = bottom_contacts[layer_idx] = &layer_allocate( + layer_storage, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx); + bottom_contact_layer->polygons = std::move(bottom_contact_polygons); + } + if (! base_layer_polygons.empty()) { + SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate( + layer_storage, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx); + base_layer->polygons = union_(base_layer_polygons); + } + throw_on_cancel(); } }, tbb::simple_partitioner()); - - //FIXME simplify! - return support_layer_storage; } /*! @@ -4538,9 +4616,12 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume SupportGeneratorLayerStorage layer_storage; SupportGeneratorLayersPtr top_contacts; SupportGeneratorLayersPtr bottom_contacts; - SupportGeneratorLayersPtr top_interface_layers; + SupportGeneratorLayersPtr interface_layers; + SupportGeneratorLayersPtr base_interface_layers; SupportGeneratorLayersPtr intermediate_layers; + SupportParameters support_params(print_object); + if (size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel); num_support_layers > 0) { @@ -4550,13 +4631,15 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume std::vector move_bounds(num_support_layers); // ### Place tips of the support tree - top_contacts .assign(num_support_layers, nullptr); - bottom_contacts .assign(num_support_layers, nullptr); - top_interface_layers.assign(num_support_layers, nullptr); - intermediate_layers .assign(num_support_layers, nullptr); + bottom_contacts .assign(num_support_layers, nullptr); + top_contacts .assign(num_support_layers, nullptr); + interface_layers .assign(num_support_layers, nullptr); + base_interface_layers.assign(num_support_layers, nullptr); + intermediate_layers .assign(num_support_layers, nullptr); for (size_t mesh_idx : processing.second) - generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, move_bounds, top_contacts, top_interface_layers, layer_storage, throw_on_cancel); + generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, + move_bounds, top_contacts, interface_layers, base_interface_layers, layer_storage, throw_on_cancel); auto t_gen = std::chrono::high_resolution_clock::now(); #ifdef TREESUPPORT_DEBUG_SVG @@ -4587,12 +4670,24 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); else { assert(print_object.config().support_material_style == smsOrganic); - std::vector support_layer_storage = draw_branches(*print.get_object(processing.second.front()), volumes, config, move_bounds, throw_on_cancel); - std::vector support_roof_storage(support_layer_storage.size()); - finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage, - bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); + draw_branches( + *print.get_object(processing.second.front()), volumes, config, move_bounds, + bottom_contacts, top_contacts, intermediate_layers, layer_storage, + throw_on_cancel); } + auto remove_undefined_layers = [](SupportGeneratorLayersPtr& layers) { + layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end()); + }; + remove_undefined_layers(bottom_contacts); + remove_undefined_layers(top_contacts); + remove_undefined_layers(interface_layers); + remove_undefined_layers(base_interface_layers); + remove_undefined_layers(intermediate_layers); + + std::tie(interface_layers, base_interface_layers) = generate_interface_layers(print_object.config(), support_params, + bottom_contacts, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); + auto t_draw = std::chrono::high_resolution_clock::now(); auto dur_pre_gen = 0.001 * std::chrono::duration_cast(t_precalc - t_start).count(); auto dur_gen = 0.001 * std::chrono::duration_cast(t_gen - t_precalc).count(); @@ -4618,18 +4713,10 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume continue; } - auto remove_undefined_layers = [](SupportGeneratorLayersPtr &layers) { - layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end()); - }; - remove_undefined_layers(bottom_contacts); - remove_undefined_layers(top_contacts); - remove_undefined_layers(intermediate_layers); - // Produce the support G-code. // Used by both classic and tree supports. - SupportParameters support_params(print_object); - SupportGeneratorLayersPtr interface_layers, base_interface_layers; - SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); + SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), + top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); #if 1 //#ifdef SLIC3R_DEBUG SupportGeneratorLayersPtr layers_sorted = #endif // SLIC3R_DEBUG diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 2ed5d50ec3..899f027242 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -364,10 +364,6 @@ public: * \brief Amount of layers distance required from the top of the model to the bottom of a support structure. */ size_t z_distance_bottom_layers; - /*! - * \brief used for performance optimization at the support floor. Should have no impact on the resulting tree. - */ - size_t performance_interface_skip_layers; /*! * \brief User specified angles for the support infill. */ From c838fc92fc1ebb19791e003dd4481f21d5ee12c8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 8 May 2023 10:25:28 +0200 Subject: [PATCH 67/70] Follow-up to 1e7a3216ca87d9fdeaa5b12f813e5900a77d49bc WIP Organic supports intefaces: bugfixes --- src/libslic3r/Support/SupportCommon.cpp | 37 +++++++++++++------------ src/libslic3r/Support/TreeSupport.cpp | 13 +++++++-- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 4978b9e40c..951ae740df 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -137,7 +137,8 @@ std::pair generate_interfa if (! intermediate_layers.empty() && support_params.has_interfaces()) { // For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers. BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start"; - const bool snug_supports = config.support_material_style.value != smsGrid; + const bool snug_supports = config.support_material_style.value == smsSnug; + const bool smooth_supports = config.support_material_style.value != smsGrid; SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first; SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second; interface_layers.assign(intermediate_layers.size(), nullptr); @@ -147,7 +148,7 @@ std::pair generate_interfa const auto minimum_island_radius = support_params.support_material_interface_flow.scaled_spacing() / support_params.interface_density; const auto closing_distance = smoothing_distance; // scaled(config.support_material_closing_radius.value); // Insert a new layer into base_interface_layers, if intersection with base exists. - auto insert_layer = [&layer_storage, snug_supports, closing_distance, smoothing_distance, minimum_island_radius]( + auto insert_layer = [&layer_storage, smooth_supports, closing_distance, smoothing_distance, minimum_island_radius]( SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* { bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty(); @@ -156,14 +157,16 @@ std::pair generate_interfa append(bottom, std::move(top)); // Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners). bottom = intersection( - snug_supports ? + smooth_supports ? smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : union_safety_offset(std::move(bottom)), intermediate_layer.polygons); - if (has_top_interface) + if (has_top_interface) { // Don't trim the precomputed Organic supports top interface with base layer // as the precomputed top interface likely expands over multiple tree tips. bottom = union_(std::move(top_interface_layer->polygons), bottom); + top_interface_layer->polygons.clear(); + } if (! bottom.empty()) { //FIXME Remove non-printable tiny islands, let them be printed using the base support. //bottom = opening(std::move(bottom), minimum_island_radius); @@ -253,13 +256,8 @@ std::pair generate_interfa auto resolve_same_layer = [](SupportGeneratorLayersPtr &layers, int &idx, coordf_t print_z) -> SupportGeneratorLayer* { if (! layers.empty()) { idx = idx_higher_or_equal(layers, idx, [print_z](const SupportGeneratorLayer *layer) { return layer->print_z > print_z - EPSILON; }); - if (idx < int(layers.size()) && layers[idx]->print_z < print_z + EPSILON) { - SupportGeneratorLayer *l = layers[idx]; - // Remove the layer from the source container, as it will be consumed here: It will be merged - // with the newly produced interfaces. - layers[idx] = nullptr; - return l; - } + if (idx < int(layers.size()) && layers[idx]->print_z < print_z + EPSILON) + return layers[idx]; } return nullptr; }; @@ -284,11 +282,14 @@ std::pair generate_interfa // Compress contact_out, remove the nullptr items. // The parallel_for above may not have merged all the interface and base_interface layers // generated by the Organic supports code, do it here. - auto merge_remove_nulls = [](SupportGeneratorLayersPtr &in1, SupportGeneratorLayersPtr &in2) { - size_t nonzeros = std::count_if(in1.begin(), in1.end(), [](auto *l) { return l != nullptr; }) + - std::count_if(in2.begin(), in2.end(), [](auto *l) { return l != nullptr; }); - remove_nulls(in1); - remove_nulls(in2); + auto merge_remove_empty = [](SupportGeneratorLayersPtr &in1, SupportGeneratorLayersPtr &in2) { + auto remove_empty = [](SupportGeneratorLayersPtr &vec) { + vec.erase( + std::remove_if(vec.begin(), vec.end(), [](const SupportGeneratorLayer *ptr) { return ptr == nullptr || ptr->polygons.empty(); }), + vec.end()); + }; + remove_empty(in1); + remove_empty(in2); if (in2.empty()) return std::move(in1); else if (in1.empty()) @@ -299,8 +300,8 @@ std::pair generate_interfa return std::move(out); } }; - interface_layers = merge_remove_nulls(interface_layers, top_interface_layers); - base_interface_layers = merge_remove_nulls(base_interface_layers, top_base_interface_layers); + interface_layers = merge_remove_empty(interface_layers, top_interface_layers); + base_interface_layers = merge_remove_empty(base_interface_layers, top_base_interface_layers); BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end"; } diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index dfeb3fdf94..e276613842 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1119,7 +1119,7 @@ public: void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof) { std::lock_guard lock(m_mutex_layer_storage); - this->add_roof_unguarded(std::move(overhang_areas), 0, dtt_roof); + this->add_roof_unguarded(std::move(overhang_areas), 0, std::min(dtt_roof, this->support_parameters.num_top_interface_layers)); } // called by sample_overhang_area() @@ -1204,6 +1204,8 @@ public: private: void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof) { + assert(support_parameters.has_top_contacts); + assert(dtt_roof <= support_parameters.num_top_interface_layers); SupportGeneratorLayersPtr &layers = dtt_roof == 0 ? this->top_contacts : dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces; @@ -4413,8 +4415,13 @@ static void draw_branches( bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {})); if (area(this_bottom_contacts) < support_area_min) bottom_extra_slices.pop_back(); - else if (config.settings.support_floor_layers > 0) - bottom_contacts.emplace_back(std::move(this_bottom_contacts)); + else { + // At least a fraction of the tree bottom is considered to be supported. + if (config.settings.support_floor_layers > 0) + // Turn this fraction of the tree bottom into a contact layer. + bottom_contacts.emplace_back(std::move(this_bottom_contacts)); + break; + } } if (config.settings.support_floor_layers > 0) for (int i = int(bottom_extra_slices.size()) - 2; i >= 0; -- i) From 9d495f2413c215c80de9eead736e78c1c9142453 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 9 May 2023 09:46:27 +0200 Subject: [PATCH 68/70] Organic supports: Refactoring for adding interfaces to tree tips. --- src/libslic3r/Support/TreeSupport.cpp | 549 ++++++++++++++------------ 1 file changed, 290 insertions(+), 259 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index e276613842..0eb009c777 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -997,11 +997,245 @@ inline SupportGeneratorLayer& layer_allocate( return layer_initialize(layer, slicing_params, config, layer_idx); } +using SupportElements = std::deque; + +// Used by generate_initial_areas() in parallel by multiple layers. +class InterfacePlacer { +public: + InterfacePlacer( + const SlicingParameters &slicing_parameters, + const SupportParameters &support_parameters, + const TreeSupportSettings &config, + SupportGeneratorLayerStorage &layer_storage, + SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayersPtr &top_interfaces, + SupportGeneratorLayersPtr &top_base_interfaces) + : + slicing_parameters(slicing_parameters), support_parameters(support_parameters), config(config), + layer_storage(layer_storage), top_contacts(top_contacts), top_interfaces(top_interfaces), top_base_interfaces(top_base_interfaces) + {} + InterfacePlacer(const InterfacePlacer& rhs) : + slicing_parameters(rhs.slicing_parameters), support_parameters(rhs.support_parameters), config(rhs.config), + layer_storage(rhs.layer_storage), top_contacts(rhs.top_contacts), top_interfaces(rhs.top_interfaces), top_base_interfaces(rhs.top_base_interfaces) + {} + + const SlicingParameters &slicing_parameters; + const SupportParameters &support_parameters; + const TreeSupportSettings &config; + SupportGeneratorLayersPtr& top_contacts_mutable() { return this->top_contacts; } + +public: + // Insert the contact layer and some of the inteface and base interface layers below. + void add_roofs(std::vector &&new_roofs, const size_t insert_layer_idx) + { + if (! new_roofs.empty()) { + std::lock_guard lock(m_mutex_layer_storage); + for (size_t idx = 0; idx < new_roofs.size(); ++ idx) + if (! new_roofs[idx].empty()) + add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx, idx); + } + } + + void add_roof(Polygons &&new_roof, const size_t insert_layer_idx, const size_t dtt_tip) + { + std::lock_guard lock(m_mutex_layer_storage); + add_roof_unguarded(std::move(new_roof), insert_layer_idx, dtt_tip); + } + + // called by sample_overhang_area() + void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof) + { + std::lock_guard lock(m_mutex_layer_storage); + this->add_roof_unguarded(std::move(overhang_areas), 0, std::min(dtt_roof, this->support_parameters.num_top_interface_layers)); + } + + void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof) + { + assert(support_parameters.has_top_contacts); + assert(dtt_roof <= support_parameters.num_top_interface_layers); + SupportGeneratorLayersPtr &layers = + dtt_roof == 0 ? this->top_contacts : + dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces; + SupportGeneratorLayer*& l = layers[insert_layer_idx]; + if (l == nullptr) + l = &layer_allocate_unguarded(layer_storage, dtt_roof == 0 ? SupporLayerType::TopContact : SupporLayerType::TopInterface, + slicing_parameters, config, insert_layer_idx); + // will be unioned in finalize_interface_and_support_areas() + append(l->polygons, std::move(new_roofs)); + } + +private: + // Outputs + SupportGeneratorLayerStorage &layer_storage; + SupportGeneratorLayersPtr &top_contacts; + SupportGeneratorLayersPtr &top_interfaces; + SupportGeneratorLayersPtr &top_base_interfaces; + + // Mutexes, guards + std::mutex m_mutex_layer_storage; +}; + +class RichInterfacePlacer : public InterfacePlacer { +public: + RichInterfacePlacer( + const InterfacePlacer &interface_placer, + const TreeModelVolumes &volumes, + bool force_tip_to_roof, + size_t num_support_layers, + std::vector &move_bounds) + : + InterfacePlacer(interface_placer), + volumes(volumes), force_tip_to_roof(force_tip_to_roof), move_bounds(move_bounds) + { + m_already_inserted.assign(num_support_layers, {}); + this->min_xy_dist = this->config.xy_distance > this->config.xy_min_distance; + } + const TreeModelVolumes &volumes; + // Radius of the tree tip is large enough to be covered by an interface. + const bool force_tip_to_roof; + bool min_xy_dist; + +public: + // called by sample_overhang_area() + void add_points_along_lines( + // Insert points (tree tips or top contact interfaces) along these lines. + LineInformations lines, + // Start at this layer. + LayerIndex insert_layer_idx, + // Insert this number of interface layers. + size_t roof_tip_layers, + // True if an interface is already generated above these lines. + size_t supports_roof_layers, + // The element tries to not move until this dtt is reached. + size_t dont_move_until) + { + validate_range(lines); + // Add tip area as roof (happens when minimum roof area > minimum tip area) if possible + size_t dtt_roof_tip; + for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; ++ dtt_roof_tip) { + size_t this_layer_idx = insert_layer_idx - dtt_roof_tip; + auto evaluateRoofWillGenerate = [&](const std::pair &p) { + //FIXME Vojtech: The circle is just shifted, it has a known size, the infill should fit all the time! + #if 0 + Polygon roof_circle; + for (Point corner : base_circle) + roof_circle.points.emplace_back(p.first + corner * config.min_radius); + return !generate_support_infill_lines({ roof_circle }, config, true, insert_layer_idx - dtt_roof_tip, config.support_roof_line_distance).empty(); + #else + return true; + #endif + }; + + { + std::pair split = + // keep all lines that are still valid on the next layer + split_lines(lines, [this, this_layer_idx](const std::pair &p) + { return evaluate_point_for_next_layer_function(volumes, config, this_layer_idx, p); }); + LineInformations points = std::move(split.second); + // Not all roofs are guaranteed to actually generate lines, so filter these out and add them as points. + split = split_lines(split.first, evaluateRoofWillGenerate); + lines = std::move(split.first); + append(points, split.second); + // add all points that would not be valid + for (const LineInformation &line : points) + for (const std::pair &point_data : line) + add_point_as_influence_area(point_data, this_layer_idx, + // don't move until + roof_tip_layers - dtt_roof_tip, + // supports roof + dtt_roof_tip + supports_roof_layers > 0, + // disable ovalization + false); + } + + // add all tips as roof to the roof storage + Polygons new_roofs; + for (const LineInformation &line : lines) + //FIXME sweep the tip radius along the line? + for (const std::pair &p : line) { + Polygon roof_circle{ m_base_circle }; + roof_circle.scale(config.min_radius / m_base_radius); + roof_circle.translate(p.first); + new_roofs.emplace_back(std::move(roof_circle)); + } + this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers); + } + + for (const LineInformation &line : lines) { + // If a line consists of enough tips, the assumption is that it is not a single tip, but part of a simulated support pattern. + // Ovalisation should be disabled for these to improve the quality of the lines when tip_diameter=line_width + bool disable_ovalistation = config.min_radius < 3 * config.support_line_width && roof_tip_layers == 0 && dtt_roof_tip == 0 && line.size() > 5; + for (const std::pair &point_data : line) + add_point_as_influence_area(point_data, insert_layer_idx - dtt_roof_tip, + // don't move until + dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, + // supports roof + dtt_roof_tip + supports_roof_layers > 0, + disable_ovalistation); + } + } + +private: + // called by this->add_points_along_lines() + void add_point_as_influence_area(std::pair p, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation) + { + bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE; + bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE; + bool safe_radius = p.second == LineStatus::TO_BP_SAFE || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE; + if (! config.support_rests_on_model && ! to_bp) { + BOOST_LOG_TRIVIAL(warning) << "Tried to add an invalid support point"; + tree_supports_show_error("Unable to add tip. Some overhang may not be supported correctly."sv, true); + return; + } + Polygons circle{ m_base_circle }; + circle.front().translate(p.first); + { + Point hash_pos = p.first / ((config.min_radius + 1) / 10); + std::lock_guard critical_section_movebounds(m_mutex_movebounds); + if (!m_already_inserted[insert_layer].count(hash_pos)) { + // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing + m_already_inserted[insert_layer].emplace(hash_pos); + static constexpr const size_t dtt = 0; + SupportElementState state; + state.target_height = insert_layer; + state.target_position = p.first; + state.next_position = p.first; + state.layer_idx = insert_layer; + state.effective_radius_height = dtt; + state.to_buildplate = to_bp; + state.distance_to_top = dtt; + state.result_on_layer = p.first; + assert(state.result_on_layer_is_set()); + state.increased_to_model_radius = 0; + state.to_model_gracious = gracious; + state.elephant_foot_increases = 0; + state.use_min_xy_dist = min_xy_dist; + state.supports_roof = roof; + state.dont_move_until = dont_move_until; + state.can_use_safe_radius = safe_radius; + state.missing_roof_layers = force_tip_to_roof ? dont_move_until : 0; + state.skip_ovalisation = skip_ovalisation; + move_bounds[insert_layer].emplace_back(state, std::move(circle)); + } + } + } + + // Outputs + std::vector &move_bounds; + + // Temps + static constexpr const auto m_base_radius = scaled(0.01); + const Polygon m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) }; + + // Mutexes, guards + std::mutex m_mutex_movebounds; + std::vector> m_already_inserted; +}; + int generate_raft_contact( const PrintObject &print_object, const TreeSupportSettings &config, - SupportGeneratorLayersPtr &top_contacts, - SupportGeneratorLayerStorage &layer_storage) + InterfacePlacer &interface_placer) { int raft_contact_layer_idx = -1; if (print_object.has_raft() && print_object.layer_count() > 0) { @@ -1012,17 +1246,13 @@ int generate_raft_contact( while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON) -- raft_contact_layer_idx; // Create the raft contact layer. - SupportGeneratorLayer &raft_contact_layer = layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx); - top_contacts[raft_contact_layer_idx] = &raft_contact_layer; const ExPolygons &lslices = print_object.get_layer(0)->lslices; double expansion = print_object.config().raft_expansion.value; - raft_contact_layer.polygons = expansion > 0 ? expand(lslices, scaled(expansion)) : to_polygons(lslices); + interface_placer.add_roof_unguarded(expansion > 0 ? expand(lslices, scaled(expansion)) : to_polygons(lslices), raft_contact_layer_idx, 0); } return raft_contact_layer_idx; } -using SupportElements = std::deque; - void finalize_raft_contact( const PrintObject &print_object, const int raft_contact_layer_idx, @@ -1076,215 +1306,6 @@ void finalize_raft_contact( } } -// Used by generate_initial_areas() in parallel by multiple layers. -class InterfacePlacer { -public: - InterfacePlacer( - const SlicingParameters &slicing_parameters, - const SupportParameters &support_parameters, - const TreeModelVolumes &volumes, const TreeSupportSettings &config, - bool force_tip_to_roof, size_t num_support_layers, - std::vector &move_bounds, - SupportGeneratorLayerStorage &layer_storage, - SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &top_interfaces, SupportGeneratorLayersPtr &top_base_interfaces) - : - slicing_parameters(slicing_parameters), support_parameters(support_parameters), volumes(volumes), config(config), force_tip_to_roof(force_tip_to_roof), - move_bounds(move_bounds), - layer_storage(layer_storage), top_contacts(top_contacts), top_interfaces(top_interfaces), top_base_interfaces(top_base_interfaces) { - m_already_inserted.assign(num_support_layers, {}); - this->min_xy_dist = config.xy_distance > config.xy_min_distance; - } - const SlicingParameters &slicing_parameters; - const SupportParameters &support_parameters; - const TreeModelVolumes &volumes; - const TreeSupportSettings &config; - // Radius of the tree tip is large enough to be covered by an interface. - const bool force_tip_to_roof; - bool min_xy_dist; - -public: - // called by sample_overhang_area() - // Insert the contact layer and some of the inteface and base interface layers below. - void add_roofs(std::vector &&new_roofs, const size_t insert_layer_idx) - { - if (! new_roofs.empty()) { - std::lock_guard lock(m_mutex_layer_storage); - for (size_t idx = 0; idx < new_roofs.size(); ++ idx) - if (! new_roofs[idx].empty()) - add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx, idx); - } - } - - // called by sample_overhang_area() - void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof) - { - std::lock_guard lock(m_mutex_layer_storage); - this->add_roof_unguarded(std::move(overhang_areas), 0, std::min(dtt_roof, this->support_parameters.num_top_interface_layers)); - } - - // called by sample_overhang_area() - void add_points_along_lines( - // Insert points (tree tips or top contact interfaces) along these lines. - LineInformations lines, - // Start at this layer. - LayerIndex insert_layer_idx, - // Insert this number of interface layers. - size_t roof_tip_layers, - // True if an interface is already generated above these lines. - size_t supports_roof_layers, - // The element tries to not move until this dtt is reached. - size_t dont_move_until) - { - validate_range(lines); - // Add tip area as roof (happens when minimum roof area > minimum tip area) if possible - size_t dtt_roof_tip; - for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; ++ dtt_roof_tip) { - size_t this_layer_idx = insert_layer_idx - dtt_roof_tip; - auto evaluateRoofWillGenerate = [&](const std::pair &p) { - //FIXME Vojtech: The circle is just shifted, it has a known size, the infill should fit all the time! - #if 0 - Polygon roof_circle; - for (Point corner : base_circle) - roof_circle.points.emplace_back(p.first + corner * config.min_radius); - return !generate_support_infill_lines({ roof_circle }, config, true, insert_layer_idx - dtt_roof_tip, config.support_roof_line_distance).empty(); - #else - return true; - #endif - }; - - { - std::pair split = - // keep all lines that are still valid on the next layer - split_lines(lines, [this, this_layer_idx](const std::pair &p) - { return evaluate_point_for_next_layer_function(volumes, config, this_layer_idx, p); }); - LineInformations points = std::move(split.second); - // Not all roofs are guaranteed to actually generate lines, so filter these out and add them as points. - split = split_lines(split.first, evaluateRoofWillGenerate); - lines = std::move(split.first); - append(points, split.second); - // add all points that would not be valid - for (const LineInformation &line : points) - for (const std::pair &point_data : line) - add_point_as_influence_area(point_data, this_layer_idx, - // don't move until - roof_tip_layers - dtt_roof_tip, - // supports roof - dtt_roof_tip > 0, - // disable ovalization - false); - } - - // add all tips as roof to the roof storage - Polygons new_roofs; - for (const LineInformation &line : lines) - //FIXME sweep the tip radius along the line? - for (const std::pair &p : line) { - Polygon roof_circle{ m_base_circle }; - roof_circle.scale(config.min_radius / m_base_radius); - roof_circle.translate(p.first); - new_roofs.emplace_back(std::move(roof_circle)); - } - this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers); - } - - for (const LineInformation &line : lines) { - // If a line consists of enough tips, the assumption is that it is not a single tip, but part of a simulated support pattern. - // Ovalisation should be disabled for these to improve the quality of the lines when tip_diameter=line_width - bool disable_ovalistation = config.min_radius < 3 * config.support_line_width && roof_tip_layers == 0 && dtt_roof_tip == 0 && line.size() > 5; - for (const std::pair &point_data : line) - add_point_as_influence_area(point_data, insert_layer_idx - dtt_roof_tip, - // don't move until - dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0, - // supports roof - dtt_roof_tip + supports_roof_layers > 0, - disable_ovalistation); - } - } - -private: - void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof) - { - assert(support_parameters.has_top_contacts); - assert(dtt_roof <= support_parameters.num_top_interface_layers); - SupportGeneratorLayersPtr &layers = - dtt_roof == 0 ? this->top_contacts : - dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces; - SupportGeneratorLayer*& l = layers[insert_layer_idx]; - if (l == nullptr) - l = &layer_allocate_unguarded(layer_storage, dtt_roof == 0 ? SupporLayerType::TopContact : SupporLayerType::TopInterface, - slicing_parameters, config, insert_layer_idx); - // will be unioned in finalize_interface_and_support_areas() - append(l->polygons, std::move(new_roofs)); - } - - // called by this->add_points_along_lines() - void add_roof(Polygons &&new_roof, const size_t insert_layer_idx, const size_t dtt_tip) - { - std::lock_guard lock(m_mutex_layer_storage); - add_roof_unguarded(std::move(new_roof), insert_layer_idx, dtt_tip); - } - - // called by this->add_points_along_lines() - void add_point_as_influence_area(std::pair p, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation) - { - bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE; - bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE; - bool safe_radius = p.second == LineStatus::TO_BP_SAFE || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE; - if (! config.support_rests_on_model && ! to_bp) { - BOOST_LOG_TRIVIAL(warning) << "Tried to add an invalid support point"; - tree_supports_show_error("Unable to add tip. Some overhang may not be supported correctly."sv, true); - return; - } - Polygons circle{ m_base_circle }; - circle.front().translate(p.first); - { - Point hash_pos = p.first / ((config.min_radius + 1) / 10); - std::lock_guard critical_section_movebounds(m_mutex_movebounds); - if (!m_already_inserted[insert_layer].count(hash_pos)) { - // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing - m_already_inserted[insert_layer].emplace(hash_pos); - static constexpr const size_t dtt = 0; - SupportElementState state; - state.target_height = insert_layer; - state.target_position = p.first; - state.next_position = p.first; - state.layer_idx = insert_layer; - state.effective_radius_height = dtt; - state.to_buildplate = to_bp; - state.distance_to_top = dtt; - state.result_on_layer = p.first; - assert(state.result_on_layer_is_set()); - state.increased_to_model_radius = 0; - state.to_model_gracious = gracious; - state.elephant_foot_increases = 0; - state.use_min_xy_dist = min_xy_dist; - state.supports_roof = roof; - state.dont_move_until = dont_move_until; - state.can_use_safe_radius = safe_radius; - state.missing_roof_layers = force_tip_to_roof ? dont_move_until : 0; - state.skip_ovalisation = skip_ovalisation; - move_bounds[insert_layer].emplace_back(state, std::move(circle)); - } - } - } - - // Outputs - std::vector &move_bounds; - SupportGeneratorLayerStorage &layer_storage; - SupportGeneratorLayersPtr &top_contacts; - SupportGeneratorLayersPtr &top_interfaces; - SupportGeneratorLayersPtr &top_base_interfaces; - - // Temps - static constexpr const auto m_base_radius = scaled(0.01); - const Polygon m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) }; - - // Mutexes, guards - std::mutex m_mutex_movebounds; - std::mutex m_mutex_layer_storage; - std::vector> m_already_inserted; -}; - // Called by generate_initial_areas(), used in parallel by multiple layers. // Produce // 1) Maximum num_support_roof_layers roof (top interface & contact) layers. @@ -1309,7 +1330,7 @@ void sample_overhang_area( // Configuration classes const TreeSupportMeshGroupSettings &mesh_group_settings, // Configuration & Output - InterfacePlacer &interface_placer) + RichInterfacePlacer &interface_placer) { // Assumption is that roof will support roof further up to avoid a lot of unnecessary branches. Each layer down it is checked whether the roof area // is still large enough to be a roof and aborted as soon as it is not. This part was already reworked a few times, and there could be an argument @@ -1447,17 +1468,11 @@ static void generate_initial_areas( const TreeSupportSettings &config, const std::vector &overhangs, std::vector &move_bounds, - SupportGeneratorLayersPtr &top_contacts, - SupportGeneratorLayersPtr &top_interfaces, - SupportGeneratorLayersPtr &top_base_interfaces, - SupportGeneratorLayerStorage &layer_storage, + InterfacePlacer &interface_placer, std::function throw_on_cancel) { using AvoidanceType = TreeModelVolumes::AvoidanceType; TreeSupportMeshGroupSettings mesh_group_settings(print_object); - SupportParameters support_params(print_object); - support_params.with_sheath = true; - support_params.support_density = 0; // To ensure z_distance_top_layers are left empty between the overhang (zeroth empty layer), the support has to be added z_distance_top_layers+1 layers below const size_t z_distance_delta = config.z_distance_top_layers + 1; @@ -1487,7 +1502,7 @@ static void generate_initial_areas( ; const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers; const bool roof_enabled = num_support_roof_layers > 0; - const bool force_tip_to_roof = roof_enabled && sqr(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area; + const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.soluble_interface || sqr(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area); // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point // may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang // does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. @@ -1513,7 +1528,7 @@ static void generate_initial_areas( const size_t num_raft_layers = config.raft_layers.size(); const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1); num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta))); - raft_contact_layer_idx = generate_raft_contact(print_object, config, top_contacts, layer_storage); + raft_contact_layer_idx = generate_raft_contact(print_object, config, interface_placer); // Enumerate layers for which the support tips may be generated from overhangs above. raw_overhangs.reserve(num_support_layers - first_support_layer); for (size_t layer_idx = first_support_layer; layer_idx < num_support_layers; ++ layer_idx) @@ -1521,14 +1536,12 @@ static void generate_initial_areas( raw_overhangs.push_back({ layer_idx, &overhangs[overhang_idx] }); } - InterfacePlacer interface_placer{ print_object.slicing_parameters(), support_params, volumes, config, force_tip_to_roof, num_support_layers, - // Outputs - move_bounds, layer_storage, top_contacts, top_interfaces, top_base_interfaces }; + RichInterfacePlacer rich_interface_placer{ interface_placer, volumes, force_tip_to_roof, num_support_layers, move_bounds }; tbb::parallel_for(tbb::blocked_range(0, raw_overhangs.size()), - [&volumes, &config, &raw_overhangs, &mesh_group_settings, &support_params, + [&volumes, &config, &raw_overhangs, &mesh_group_settings, min_xy_dist, force_tip_to_roof, roof_enabled, num_support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag, - &interface_placer, &throw_on_cancel](const tbb::blocked_range &range) { + &rich_interface_placer, &throw_on_cancel](const tbb::blocked_range &range) { for (size_t raw_overhang_idx = range.begin(); raw_overhang_idx < range.end(); ++ raw_overhang_idx) { size_t layer_idx = raw_overhangs[raw_overhang_idx].first; const Polygons &overhang_raw = *raw_overhangs[raw_overhang_idx].second; @@ -1620,7 +1633,7 @@ static void generate_initial_areas( LineInformations fresh_valid_points = convert_lines_to_internal(volumes, config, convert_internal_to_lines(split.second), layer_idx - lag_ctr); validate_range(fresh_valid_points); - interface_placer.add_points_along_lines(fresh_valid_points, (force_tip_to_roof && lag_ctr <= num_support_roof_layers) ? num_support_roof_layers : 0, layer_idx - lag_ctr, false, roof_enabled ? num_support_roof_layers : 0); + rich_interface_placer.add_points_along_lines(fresh_valid_points, (force_tip_to_roof && lag_ctr <= num_support_roof_layers) ? num_support_roof_layers : 0, layer_idx - lag_ctr, false, roof_enabled ? num_support_roof_layers : 0); } } #endif @@ -1638,7 +1651,7 @@ static void generate_initial_areas( //check_self_intersections(overhang_regular, "overhang_regular3"); for (ExPolygon &roof_part : union_ex(overhang_roofs)) { sample_overhang_area(to_polygons(std::move(roof_part)), true, layer_idx, num_support_roof_layers, connect_length, - mesh_group_settings, interface_placer); + mesh_group_settings, rich_interface_placer); throw_on_cancel(); } } @@ -1649,13 +1662,13 @@ static void generate_initial_areas( for (ExPolygon &support_part : union_ex(overhang_regular)) { sample_overhang_area(to_polygons(std::move(support_part)), false, layer_idx, num_support_roof_layers, connect_length, - mesh_group_settings, interface_placer); + mesh_group_settings, rich_interface_placer); throw_on_cancel(); } } }); - finalize_raft_contact(print_object, raft_contact_layer_idx, top_contacts, move_bounds); + finalize_raft_contact(print_object, raft_contact_layer_idx, interface_placer.top_contacts_mutable(), move_bounds); } static unsigned int move_inside(const Polygons &polygons, Point &from, int distance = 0, int64_t maxDist2 = std::numeric_limits::max()) @@ -4172,6 +4185,7 @@ static void draw_branches( // I/O: SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, + InterfacePlacer &interface_placer, // Output: SupportGeneratorLayersPtr &intermediate_layers, @@ -4346,7 +4360,7 @@ static void draw_branches( mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive; tbb::parallel_for(tbb::blocked_range(0, trees.size(), 1), - [&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { + [&trees, &volumes, &config, &slicing_params, &move_bounds, &interface_placer, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range &range) { indexed_triangle_set partial_mesh; std::vector slice_z; std::vector bottom_contacts; @@ -4434,10 +4448,20 @@ static void draw_branches( *it_dst ++ = std::move(it_src->polygons); } } - if (branch.has_tip) { + +#if 0 + //FIXME branch.has_tip seems to not be reliable. + if (branch.has_tip && interface_placer.support_parameters.has_top_contacts) // Add top slices to top contacts / interfaces / base interfaces. - //slices; - } + for (int i = int(branch.path.size()) - 1; i >= 0; -- i) { + const SupportElement &el = *branch.path[i]; + if (el.state.missing_roof_layers == 0) + break; + //FIXME Move or not? + interface_placer.add_roof(std::move(slices[int(slices.size()) - i - 1]), el.state.layer_idx, + interface_placer.support_parameters.num_top_interface_layers + 1 - el.state.missing_roof_layers); + } +#endif } layer_begin += LayerIndex(num_empty); @@ -4619,34 +4643,44 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume std::vector overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel); // ### Precalculate avoidances, collision etc. - + size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel); + bool has_support = num_support_layers > 0; + num_support_layers = std::max(num_support_layers, config.raft_layers.size()); + + SupportParameters support_params(print_object); + support_params.with_sheath = true; + support_params.support_density = 0; + SupportGeneratorLayerStorage layer_storage; SupportGeneratorLayersPtr top_contacts; SupportGeneratorLayersPtr bottom_contacts; SupportGeneratorLayersPtr interface_layers; SupportGeneratorLayersPtr base_interface_layers; - SupportGeneratorLayersPtr intermediate_layers; + SupportGeneratorLayersPtr intermediate_layers(num_support_layers, nullptr); + if (support_params.has_top_contacts) + top_contacts.assign(num_support_layers, nullptr); + if (support_params.has_bottom_contacts) + bottom_contacts.assign(num_support_layers, nullptr); + if (support_params.has_interfaces()) + interface_layers.assign(num_support_layers, nullptr); + if (support_params.has_base_interfaces()) + base_interface_layers.assign(num_support_layers, nullptr); - SupportParameters support_params(print_object); - - if (size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel); - num_support_layers > 0) { + InterfacePlacer interface_placer{ + print_object.slicing_parameters(), support_params, config, + // Outputs + layer_storage, top_contacts, interface_layers, base_interface_layers }; + if (has_support) { auto t_precalc = std::chrono::high_resolution_clock::now(); // value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in draw_areas std::vector move_bounds(num_support_layers); // ### Place tips of the support tree - bottom_contacts .assign(num_support_layers, nullptr); - top_contacts .assign(num_support_layers, nullptr); - interface_layers .assign(num_support_layers, nullptr); - base_interface_layers.assign(num_support_layers, nullptr); - intermediate_layers .assign(num_support_layers, nullptr); - for (size_t mesh_idx : processing.second) generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, - move_bounds, top_contacts, interface_layers, base_interface_layers, layer_storage, throw_on_cancel); + move_bounds, interface_placer, throw_on_cancel); auto t_gen = std::chrono::high_resolution_clock::now(); #ifdef TREESUPPORT_DEBUG_SVG @@ -4679,7 +4713,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume assert(print_object.config().support_material_style == smsOrganic); draw_branches( *print.get_object(processing.second.front()), volumes, config, move_bounds, - bottom_contacts, top_contacts, intermediate_layers, layer_storage, + bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage, throw_on_cancel); } @@ -4713,12 +4747,9 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume // BOOST_LOG_TRIVIAL(error) << "Why ask questions when you already know the answer twice.\n (This is not a real bug, please dont report it.)"; move_bounds.clear(); - } else { - top_contacts.assign(config.raft_layers.size(), nullptr); - if (generate_raft_contact(print_object, config, top_contacts, layer_storage) < 0) - // No raft. - continue; - } + } else if (generate_raft_contact(print_object, config, interface_placer) < 0) + // No raft. + continue; // Produce the support G-code. // Used by both classic and tree supports. From 84db6356b3c7a27a5c1bd2762fa93a17ed9bbe00 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 9 May 2023 10:46:56 +0200 Subject: [PATCH 69/70] Organic Supports: support_tree_branch_diameter_double_wall to control when the 2nd wall kicks in. --- src/libslic3r/Preset.cpp | 3 +- src/libslic3r/PrintConfig.cpp | 12 +++++++ src/libslic3r/PrintConfig.hpp | 1 + src/libslic3r/PrintObject.cpp | 1 + src/libslic3r/Support/SupportCommon.cpp | 35 +++++++++++---------- src/libslic3r/Support/SupportParameters.cpp | 2 ++ src/libslic3r/Support/SupportParameters.hpp | 2 ++ src/slic3r/GUI/ConfigManipulation.cpp | 3 +- src/slic3r/GUI/Tab.cpp | 1 + 9 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 23f1438c4c..d20514bba3 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -444,7 +444,8 @@ static std::vector s_Preset_print_options { "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", "support_material_bottom_contact_distance", "support_material_buildplate_only", - "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter", + "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall", + "support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder", "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d48f18aa5d..77120bfdfd 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2939,6 +2939,18 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(5)); + def = this->add("support_tree_branch_diameter_double_wall", coFloat); + def->label = L("Branch Diameter with double walls"); + def->category = L("Support material"); + // TRN PrintSettings: "Organic supports" > "Branch Diameter" + def->tooltip = L("Branches with area larger than the area of a circle of this diameter will be printed with double walls for stability. " + "Set this value to zero for no double walls."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 100.f; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(3)); + // Tree Support Branch Distance // How far apart the branches need to be when they touch the model. Making this distance small will cause // the tree support to touch the model at more points, causing better overhang but making support harder to remove. diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index b9ca95a150..2a4b3258da 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -554,6 +554,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, support_tree_angle_slow)) ((ConfigOptionFloat, support_tree_branch_diameter)) ((ConfigOptionFloat, support_tree_branch_diameter_angle)) + ((ConfigOptionFloat, support_tree_branch_diameter_double_wall)) ((ConfigOptionPercent, support_tree_top_rate)) ((ConfigOptionFloat, support_tree_branch_distance)) ((ConfigOptionFloat, support_tree_tip_diameter)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b43afd6be9..1c37339a23 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -712,6 +712,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "support_tree_angle_slow" || opt_key == "support_tree_branch_diameter" || opt_key == "support_tree_branch_diameter_angle" + || opt_key == "support_tree_branch_diameter_double_wall" || opt_key == "support_tree_top_rate" || opt_key == "support_tree_branch_distance" || opt_key == "support_tree_tip_diameter" diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index c01e49bffa..2035f9ea3b 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -537,7 +537,8 @@ static Polylines draw_perimeters(const ExPolygon &expoly, double clip_length) static inline void tree_supports_generate_paths( ExtrusionEntitiesPtr &dst, const Polygons &polygons, - const Flow &flow) + const Flow &flow, + const SupportParameters &support_params) { // Offset expolygon inside, returns number of expolygons collected (0 or 1). // Vertices of output paths are marked with Z = source contour index of the expoly. @@ -634,21 +635,21 @@ static inline void tree_supports_generate_paths( ClipperLib_Z::Paths anchor_candidates; for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) { std::unique_ptr eec; - double area = expoly.area(); - if (area > sqr(scaled(5.))) { - eec = std::make_unique(); - // Don't reoder internal / external loops of the same island, always start with the internal loop. - eec->no_sort = true; - // Make the tree branch stable by adding another perimeter. - ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); - if (level2.size() == 1) { - Polylines polylines; - extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), - // Disable reversal of the path, always start with the anchor, always print CCW. - false); - expoly = level2.front(); + if (support_params.tree_branch_diameter_double_wall_area_scaled > 0) + if (double area = expoly.area(); area > support_params.tree_branch_diameter_double_wall_area_scaled) { + eec = std::make_unique(); + // Don't reoder internal / external loops of the same island, always start with the internal loop. + eec->no_sort = true; + // Make the tree branch stable by adding another perimeter. + ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); + if (level2.size() == 1) { + Polylines polylines; + extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + // Disable reversal of the path, always start with the anchor, always print CCW. + false); + expoly = level2.front(); + } } - } // Try to produce one more perimeter to place the seam anchor. // First genrate a 2nd perimeter loop as a source for anchor candidates. @@ -1531,7 +1532,7 @@ void generate_support_toolpaths( support_params.with_sheath, false); } if (! tree_polygons.empty()) - tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow); + tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow, support_params); } Fill *filler = filler_interface.get(); @@ -1790,7 +1791,7 @@ void generate_support_toolpaths( sheath = true; no_sort = true; } else if (config.support_material_style == SupportMaterialStyle::smsOrganic) { - tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow); + tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow, support_params); done = true; } if (! done) diff --git a/src/libslic3r/Support/SupportParameters.cpp b/src/libslic3r/Support/SupportParameters.cpp index 531e8dcafc..09eca9610d 100644 --- a/src/libslic3r/Support/SupportParameters.cpp +++ b/src/libslic3r/Support/SupportParameters.cpp @@ -137,6 +137,8 @@ SupportParameters::SupportParameters(const PrintObject &object) assert(slicing_params.interface_raft_layers == 0); assert(slicing_params.raft_layers() == 0); } + + this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled(object_config.support_tree_branch_diameter_double_wall.value)) * M_PI; } } // namespace Slic3r diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index be38e9650a..8a63d9f3f0 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -77,6 +77,8 @@ struct SupportParameters { InfillPattern contact_fill_pattern; // Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness? bool with_sheath; + // Branches of organic supports with area larger than this threshold will be extruded with double lines. + double tree_branch_diameter_double_wall_area_scaled; float raft_angle_1st_layer; float raft_angle_base; diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 4c5b0fd8e2..f645e8a0dd 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -291,7 +291,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) (config->opt_bool("support_material") || config->opt_int("support_material_enforce_layers") > 0); for (const std::string& key : { "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", - "support_tree_branch_diameter_angle", "support_tree_tip_diameter", "support_tree_branch_distance", "support_tree_top_rate" }) + "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall", + "support_tree_tip_diameter", "support_tree_branch_distance", "support_tree_top_rate" }) toggle_field(key, has_organic_supports); for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 99843e541d..2cf4969cb8 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1536,6 +1536,7 @@ void TabPrint::build() optgroup->append_single_option_line("support_tree_angle_slow", category_path + "tree_angle_slow"); optgroup->append_single_option_line("support_tree_branch_diameter", category_path + "tree_branch_diameter"); optgroup->append_single_option_line("support_tree_branch_diameter_angle", category_path + "tree_branch_diameter_angle"); + optgroup->append_single_option_line("support_tree_branch_diameter_double_wall", category_path + "tree_branch_diameter_double_wall"); optgroup->append_single_option_line("support_tree_tip_diameter", category_path + "tree_tip_diameter"); optgroup->append_single_option_line("support_tree_branch_distance", category_path + "tree_branch_distance"); optgroup->append_single_option_line("support_tree_top_rate", category_path + "tree_top_rate"); From c9f449bcb2df5a9cce6dd7c97b78fc585e6bc757 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 9 May 2023 11:14:36 +0200 Subject: [PATCH 70/70] PlaceholderParser: fixed reporting of x value outside of the interpolated table. --- src/libslic3r/PlaceholderParser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 1dbf4120ea..348825106e 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1644,9 +1644,9 @@ namespace client } if (! evaluated) { // Clamp x into the table range with EPSILON. - if (x > table.table.front().x - EPSILON) + if (double x0 = table.table.front().x; x > x0 - EPSILON && x < x0) out.set_d(table.table.front().y); - else if (x < table.table.back().x + EPSILON) + else if (double x1 = table.table.back().x; x > x1 && x < x1 + EPSILON) out.set_d(table.table.back().y); else // The value is really outside the table range.