Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsupermerill <merill@free.fr>2022-09-13 21:48:07 +0300
committersupermerill <merill@free.fr>2022-09-13 21:48:07 +0300
commitf8c9106c0119b6373b8ace5436a756a0c0003dd8 (patch)
tree362b1e274aa2e4d7fa6ea53d75132ffbc656367c
parent516fd2c86aa7e689b7c93a206308bd6390989fb1 (diff)
parentd81e39185acb8f66ae4ab546a1d46625cb96eb09 (diff)
merge latest 2.5 arachne fixes.nightly_dev
-rw-r--r--src/libslic3r/Arachne/SkeletalTrapezoidation.cpp188
-rw-r--r--src/libslic3r/Arachne/SkeletalTrapezoidation.hpp7
-rw-r--r--src/libslic3r/Arachne/WallToolPaths.cpp62
-rw-r--r--src/libslic3r/Arachne/utils/VoronoiUtils.cpp1
-rw-r--r--src/libslic3r/Arachne/utils/VoronoiUtils.hpp5
-rw-r--r--src/libslic3r/ExtrusionEntity.hpp7
-rw-r--r--src/libslic3r/Fill/FillBase.cpp4
-rw-r--r--src/libslic3r/Fill/FillConcentric.cpp1
-rw-r--r--src/libslic3r/GCode.cpp48
-rw-r--r--src/libslic3r/GCode.hpp2
-rw-r--r--src/libslic3r/Geometry/MedialAxis.cpp39
-rw-r--r--src/libslic3r/Geometry/MedialAxis.hpp4
-rw-r--r--src/libslic3r/Geometry/VoronoiUtilsCgal.cpp7
-rw-r--r--src/libslic3r/PerimeterGenerator.cpp89
-rw-r--r--src/libslic3r/PrintConfig.cpp3
-rw-r--r--src/libslic3r/SVG.cpp25
-rw-r--r--src/libslic3r/SVG.hpp4
-rw-r--r--tests/libslic3r/test_arachne.cpp353
18 files changed, 704 insertions, 145 deletions
diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp
index eb8894222..e9afa0305 100644
--- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp
+++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp
@@ -22,8 +22,6 @@
#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX 1000 //A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing performance).
-//#define ARACHNE_DEBUG
-
namespace boost::polygon {
template<> struct geometry_concept<Slic3r::Arachne::PolygonsSegmentIndex>
@@ -46,6 +44,71 @@ template<> struct segment_traits<Slic3r::Arachne::PolygonsSegmentIndex>
namespace Slic3r::Arachne
{
+#ifdef ARACHNE_DEBUG
+static void export_graph_to_svg(const std::string &path,
+ SkeletalTrapezoidationGraph &graph,
+ const Polygons &polys,
+ const std::vector<std::shared_ptr<LineJunctions>> &edge_junctions = {},
+ const bool beat_count = true,
+ const bool transition_middles = true,
+ const bool transition_ends = true)
+{
+ const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"};
+ coordf_t stroke_width = scale_(0.03);
+ BoundingBox bbox = get_extents(polys);
+ for (const auto &node : graph.nodes)
+ bbox.merge(node.p);
+
+ bbox.offset(scale_(1.));
+
+ ::Slic3r::SVG svg(path.c_str(), bbox);
+ for (const auto &line : to_lines(polys))
+ svg.draw(line, "gray", stroke_width);
+
+ for (const auto &edge : graph.edges)
+ svg.draw(Line(edge.from->p, edge.to->p), (edge.data.centralIsSet() && edge.data.isCentral()) ? "blue" : "cyan", stroke_width);
+
+ for (const auto &line_junction : edge_junctions)
+ for (const auto &extrusion_junction : *line_junction)
+ svg.draw(extrusion_junction.p, "orange", coord_t(stroke_width * 2.));
+
+ if (beat_count) {
+ for (const auto &node : graph.nodes) {
+ svg.draw(node.p, "red", coord_t(stroke_width * 1.6));
+ svg.draw_text(node.p, std::to_string(node.data.bead_count).c_str(), "black", 1);
+ }
+ }
+
+ if (transition_middles) {
+ for (auto &edge : graph.edges) {
+ if (std::shared_ptr<std::list<SkeletalTrapezoidationEdge::TransitionMiddle>> transitions = edge.data.getTransitions(); transitions) {
+ for (auto &transition : *transitions) {
+ Line edge_line = Line(edge.to->p, edge.from->p);
+ double edge_length = edge_line.length();
+ Point pt = edge_line.a + (edge_line.vector().cast<double>() * (double(transition.pos) / edge_length)).cast<coord_t>();
+ svg.draw(pt, "magenta", coord_t(stroke_width * 1.5));
+ svg.draw_text(pt, std::to_string(transition.lower_bead_count).c_str(), "black", 1);
+ }
+ }
+ }
+ }
+
+ if (transition_ends) {
+ for (auto &edge : graph.edges) {
+ if (std::shared_ptr<std::list<SkeletalTrapezoidationEdge::TransitionEnd>> transitions = edge.data.getTransitionEnds(); transitions) {
+ for (auto &transition : *transitions) {
+ Line edge_line = Line(edge.to->p, edge.from->p);
+ double edge_length = edge_line.length();
+ Point pt = edge_line.a + (edge_line.vector().cast<double>() * (double(transition.pos) / edge_length)).cast<coord_t>();
+ svg.draw(pt, transition.is_lower_end ? "green" : "lime", coord_t(stroke_width * 1.5));
+ svg.draw_text(pt, std::to_string(transition.lower_bead_count).c_str(), "black", 1);
+ }
+ }
+ }
+ }
+}
+#endif
+
SkeletalTrapezoidation::node_t& SkeletalTrapezoidation::makeNode(vd_t::vertex_type& vd_node, Point p)
{
auto he_node_it = vd_node_to_he_node.find(&vd_node);
@@ -388,8 +451,23 @@ SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, const Bead
constructFromPolygons(polys);
}
+static bool has_finite_edge_with_non_finite_vertex(const Geometry::VoronoiDiagram &voronoi_diagram)
+{
+ for (const VoronoiUtils::vd_t::edge_type &edge : voronoi_diagram.edges()) {
+ if (edge.is_finite()) {
+ assert(edge.vertex0() != nullptr && edge.vertex1() != nullptr);
+ if (edge.vertex0() == nullptr || edge.vertex1() == nullptr || !VoronoiUtils::is_finite(*edge.vertex0()) ||
+ !VoronoiUtils::is_finite(*edge.vertex1()))
+ return true;
+ }
+ }
+ return false;
+}
static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector<SkeletalTrapezoidation::Segment> &segments) {
+ if (has_finite_edge_with_non_finite_vertex(voronoi_diagram))
+ return true;
+
for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) {
if (!cell.incident_edge())
continue; // There is no spoon
@@ -408,7 +486,8 @@ static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &vorono
VoronoiUtils::vd_t::edge_type *ending_vd_edge = nullptr;
VoronoiUtils::vd_t::edge_type *edge = cell.incident_edge();
do {
- if (edge->is_infinite()) continue;
+ if (edge->is_infinite() || edge->vertex0() == nullptr || edge->vertex1() == nullptr || !VoronoiUtils::is_finite(*edge->vertex0()) || !VoronoiUtils::is_finite(*edge->vertex1()))
+ continue;
Vec2i64 v0 = VoronoiUtils::p(edge->vertex0());
Vec2i64 v1 = VoronoiUtils::p(edge->vertex1());
@@ -489,6 +568,10 @@ inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalT
void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
{
+#ifdef ARACHNE_DEBUG
+ this->outline = polys;
+#endif
+
// Check self intersections.
assert([&polys]() -> bool {
EdgeGrid::Grid grid;
@@ -517,7 +600,7 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
Geometry::VoronoiDiagram voronoi_diagram;
construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
-#ifdef ARACHNE_DEBUG
+#ifdef ARACHNE_DEBUG_VORONOI
{
static int iRun = 0;
dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys));
@@ -694,45 +777,62 @@ void SkeletalTrapezoidation::separatePointyQuadEndNodes()
// vvvvvvvvvvvvvvvvvvvvv
//
-#if 0
-static void export_graph_to_svg(const std::string &path, const SkeletalTrapezoidationGraph &graph, const Polygons &polys)
+void SkeletalTrapezoidation::generateToolpaths(std::vector<VariableWidthLines> &generated_toolpaths, bool filter_outermost_central_edges)
{
- const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"};
- coordf_t stroke_width = scale_(0.05);
- BoundingBox bbox;
- for (const auto &node : graph.nodes)
- bbox.merge(node.p);
-
- bbox.offset(scale_(1.));
- ::Slic3r::SVG svg(path.c_str(), bbox);
- for (const auto &line : to_lines(polys))
- svg.draw(line, "red", stroke_width);
-
- for (const auto &edge : graph.edges)
- svg.draw(Line(edge.from->p, edge.to->p), "cyan", scale_(0.01));
-}
+#ifdef ARACHNE_DEBUG
+ static int iRun = 0;
#endif
-void SkeletalTrapezoidation::generateToolpaths(std::vector<VariableWidthLines> &generated_toolpaths, bool filter_outermost_central_edges)
-{
p_generated_toolpaths = &generated_toolpaths;
updateIsCentral();
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-updateIsCentral-final-%d.svg", iRun), this->graph, this->outline);
+#endif
+
filterCentral(central_filter_dist);
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-filterCentral-final-%d.svg", iRun), this->graph, this->outline);
+#endif
+
if (filter_outermost_central_edges)
filterOuterCentral();
updateBeadCount();
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-updateBeadCount-final-%d.svg", iRun), this->graph, this->outline);
+#endif
+
filterNoncentralRegions();
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-filterNoncentralRegions-final-%d.svg", iRun), this->graph, this->outline);
+#endif
+
generateTransitioningRibs();
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-final-%d.svg", iRun), this->graph, this->outline);
+#endif
+
generateExtraRibs();
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-generateExtraRibs-final-%d.svg", iRun), this->graph, this->outline);
+#endif
+
generateSegments();
+
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-generateSegments-final-%d.svg", iRun), this->graph, this->outline);
+#endif
+
+#ifdef ARACHNE_DEBUG
+ ++iRun;
+#endif
}
void SkeletalTrapezoidation::updateIsCentral()
@@ -944,11 +1044,24 @@ void SkeletalTrapezoidation::generateTransitioningRibs()
filterTransitionMids();
+#ifdef ARACHNE_DEBUG
+ static int iRun = 0;
+ export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-mids-%d.svg", iRun++), this->graph, this->outline);
+#endif
+
ptr_vector_t<std::list<TransitionEnd>> edge_transition_ends; // We only map the half edge in the upward direction. mapped items are not sorted
generateAllTransitionEnds(edge_transition_ends);
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-ends-%d.svg", iRun++), this->graph, this->outline);
+#endif
+
applyTransitions(edge_transition_ends);
// Note that the shared pointer lists will be out of scope and thus destroyed here, since the remaining refs are weak_ptr.
+
+#ifdef ARACHNE_DEBUG
+ ++iRun;
+#endif
}
@@ -1670,17 +1783,38 @@ void SkeletalTrapezoidation::generateSegments()
}
}
}
-
+
+#ifdef ARACHNE_DEBUG
+ static int iRun = 0;
+ export_graph_to_svg(debug_out_path("ST-generateSegments-before-propagation-%d.svg", iRun), this->graph, this->outline);
+#endif
+
propagateBeadingsUpward(upward_quad_mids, node_beadings);
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-generateSegments-upward-propagation-%d.svg", iRun), this->graph, this->outline);
+#endif
+
propagateBeadingsDownward(upward_quad_mids, node_beadings);
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-generateSegments-downward-propagation-%d.svg", iRun), this->graph, this->outline);
+#endif
+
ptr_vector_t<LineJunctions> edge_junctions; // junctions ordered high R to low R
generateJunctions(node_beadings, edge_junctions);
+#ifdef ARACHNE_DEBUG
+ export_graph_to_svg(debug_out_path("ST-generateSegments-junctions-%d.svg", iRun), this->graph, this->outline, edge_junctions);
+#endif
+
connectJunctions(edge_junctions);
-
+
generateLocalMaximaSingleBeads();
+
+#ifdef ARACHNE_DEBUG
+ ++iRun;
+#endif
}
SkeletalTrapezoidation::edge_t* SkeletalTrapezoidation::getQuadMaxRedgeTo(edge_t* quad_start_edge)
@@ -1913,10 +2047,10 @@ void SkeletalTrapezoidation::generateJunctions(ptr_vector_t<BeadingPropagation>&
for (junction_idx = (std::max(size_t(1), beading->toolpath_locations.size()) - 1) / 2; junction_idx < num_junctions; junction_idx--)
{
coord_t bead_R = beading->toolpath_locations[junction_idx];
- // toolpath_locations computed inside DistributedBeadingStrategy be off by 1 because of rounding errors.
+ // toolpath_locations computed inside DistributedBeadingStrategy could be off by 1 because of rounding errors.
// In GH issue #8472, these roundings errors caused missing the middle extrusion.
- // Adding some epsilon should help resolve those cases.
- if (bead_R <= start_R + scaled<coord_t>(0.005))
+ // Adding small epsilon should help resolve those cases.
+ if (bead_R <= start_R + 1)
{ // Junction coinciding with start node is used in this function call
break;
}
diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp
index 83065cf87..819b71367 100644
--- a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp
+++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp
@@ -20,6 +20,9 @@
#include "SkeletalTrapezoidationGraph.hpp"
#include "../Geometry/Voronoi.hpp"
+//#define ARACHNE_DEBUG
+//#define ARACHNE_DEBUG_VORONOI
+
namespace Slic3r::Arachne
{
@@ -123,6 +126,10 @@ public:
*/
void generateToolpaths(std::vector<VariableWidthLines> &generated_toolpaths, bool filter_outermost_central_edges = false);
+#ifdef ARACHNE_DEBUG
+ Polygons outline;
+#endif
+
protected:
/*!
* Auxiliary for referencing one transition along an edge which may contain multiple transitions
diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp
index 3d4c4c0ae..556747b14 100644
--- a/src/libslic3r/Arachne/WallToolPaths.cpp
+++ b/src/libslic3r/Arachne/WallToolPaths.cpp
@@ -332,60 +332,46 @@ void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool re
};
auto new_end = thiss.end();
- if(remove_holes)
- {
- for(auto it = thiss.begin(); it < new_end; it++)
- {
- // All polygons smaller than target are removed by replacing them with a polygon from the back of the vector
- if(fabs(ClipperLib::Area(to_path(*it))) < min_area_size)
- {
- new_end--;
+ if (remove_holes) {
+ for (auto it = thiss.begin(); it < new_end;) {
+ // All polygons smaller than target are removed by replacing them with a polygon from the back of the vector.
+ if (fabs(ClipperLib::Area(to_path(*it))) < min_area_size) {
+ --new_end;
*it = std::move(*new_end);
- it--; // wind back the iterator such that the polygon just swaped in is checked next
+ continue; // Don't increment the iterator such that the polygon just swapped in is checked next.
}
+ ++it;
}
- }
- else
- {
+ } 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<Polygon> small_holes;
- for(auto it = thiss.begin(); it < new_end; it++) {
- double area = ClipperLib::Area(to_path(*it));
- if (fabs(area) < min_area_size)
- {
- if(area >= 0)
- {
- new_end--;
- if(it < new_end) {
+ for (auto it = thiss.begin(); it < new_end;) {
+ if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) {
+ if (area >= 0) {
+ --new_end;
+ if (it < new_end) {
std::swap(*new_end, *it);
- it--;
- }
- else
- { // Don't self-swap the last Path
+ continue;
+ } else { // Don't self-swap the last Path
break;
}
- }
- else
- {
+ } else {
small_holes.push_back(*it);
}
}
+ ++it;
}
// Removes small holes that have their first point inside one of the removed outlines
// Iterating in reverse ensures that unprocessed small holes won't be moved
const auto removed_outlines_start = new_end;
- for(auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++)
- {
- for(auto outline_it = removed_outlines_start; outline_it < thiss.end() ; outline_it++)
- {
- if(Polygon(*outline_it).contains(*hole_it->begin())) {
+ for (auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++)
+ for (auto outline_it = removed_outlines_start; outline_it < thiss.end(); outline_it++)
+ if (Polygon(*outline_it).contains(*hole_it->begin())) {
new_end--;
*hole_it = std::move(*new_end);
break;
}
- }
- }
}
thiss.resize(new_end-thiss.begin());
}
@@ -629,6 +615,14 @@ void WallToolPaths::stitchToolPaths(std::vector<VariableWidthLines> &toolpaths,
{
continue;
}
+
+ // PolylineStitcher, in some cases, produced closed extrusion (polygons),
+ // but the endpoints differ by a small distance. So we reconnect them.
+ // FIXME Lukas H.: Investigate more deeply why it is happening.
+ if (wall_polygon.junctions.front().p != wall_polygon.junctions.back().p &&
+ (wall_polygon.junctions.back().p - wall_polygon.junctions.front().p).cast<double>().norm() < stitch_distance) {
+ wall_polygon.junctions.emplace_back(wall_polygon.junctions.front());
+ }
wall_polygon.is_closed = true;
wall_lines.push_back(std::move(wall_polygon)); // add stitched polygons to result
}
diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp
index 3da556b47..82bd79523 100644
--- a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp
+++ b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp
@@ -15,6 +15,7 @@ Vec2i64 VoronoiUtils::p(const vd_t::vertex_type *node)
{
const double x = node->x();
const double y = node->y();
+ assert(std::isfinite(x) && std::isfinite(y));
assert(x <= double(std::numeric_limits<int64_t>::max()) && x >= std::numeric_limits<int64_t>::lowest());
assert(y <= double(std::numeric_limits<int64_t>::max()) && y >= std::numeric_limits<int64_t>::lowest());
return {int64_t(x + 0.5 - (x < 0)), int64_t(y + 0.5 - (y < 0))}; // Round to the nearest integer coordinates.
diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp
index e736f98bc..aa4693643 100644
--- a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp
+++ b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp
@@ -35,6 +35,11 @@ public:
* The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola.
*/
static std::vector<Point> 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)
+ {
+ return std::isfinite(vertex.x()) && std::isfinite(vertex.y());
+ }
};
} // namespace Slic3r::Arachne
diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp
index 20a50502a..abd357821 100644
--- a/src/libslic3r/ExtrusionEntity.hpp
+++ b/src/libslic3r/ExtrusionEntity.hpp
@@ -216,6 +216,7 @@ public:
static ExtrusionRole string_to_role(const std::string_view role);
};
+//FIXME: don't use that unsafe container. use a vector of sharedpointer or a ExtrusionEntityCollection.
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
class ExtrusionPath : public ExtrusionEntity
@@ -409,6 +410,9 @@ public:
ExtrusionMultiPath(const ExtrusionPaths &paths) : ExtrusionMultiEntity(paths) {};
ExtrusionMultiPath(const ExtrusionPath &path) :ExtrusionMultiEntity(path) {}
+ ExtrusionMultiPath& operator=(const ExtrusionMultiPath& rhs) { this->paths = rhs.paths; return *this; }
+ ExtrusionMultiPath& operator=(ExtrusionMultiPath&& rhs) { this->paths = std::move(rhs.paths); return *this; }
+
virtual ExtrusionMultiPath* clone() const override { return new ExtrusionMultiPath(*this); }
virtual ExtrusionMultiPath* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); }
@@ -425,6 +429,9 @@ public:
ExtrusionMultiPath3D(const ExtrusionPaths3D &paths) : ExtrusionMultiEntity(paths) {};
ExtrusionMultiPath3D(const ExtrusionPath3D &path) :ExtrusionMultiEntity(path) {}
+ ExtrusionMultiPath3D& operator=(const ExtrusionMultiPath3D& rhs) { this->paths = rhs.paths; return *this; }
+ ExtrusionMultiPath3D& operator=(ExtrusionMultiPath3D&& rhs) { this->paths = std::move(rhs.paths); return *this; }
+
virtual ExtrusionMultiPath3D* clone() const override { return new ExtrusionMultiPath3D(*this); }
virtual ExtrusionMultiPath3D* clone_move() override { return new ExtrusionMultiPath3D(std::move(*this)); }
diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp
index 8fa8e4772..d9789bd5b 100644
--- a/src/libslic3r/Fill/FillBase.cpp
+++ b/src/libslic3r/Fill/FillBase.cpp
@@ -217,8 +217,8 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams &para
for (const ExtrusionEntity* entity : entities) {
extruded_volume += entity->total_volume();
}
- //append
- all_new_paths->append(entities);
+ //append (move so the pointers are reused, and won't need to be deleted)
+ all_new_paths->append(std::move(entities));
}
thick_polylines.clear();
diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp
index 7f0bfa002..26c57c071 100644
--- a/src/libslic3r/Fill/FillConcentric.cpp
+++ b/src/libslic3r/Fill/FillConcentric.cpp
@@ -267,6 +267,7 @@ void FillConcentric::_fill_surface_single(const FillParams &params,
assert(thick_polyline.points.size() == thick_polyline.points_width.size());
}
thick_polylines_out.emplace_back(std::move(thick_polyline));
+ last_pos = thick_polylines_out.back().last_point();
}
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index ac8f8b732..8c97c5f7c 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -3855,7 +3855,13 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
const coordf_t full_loop_length = loop_to_seam.length();
const bool is_full_loop_ccw = loop_to_seam.polygon().is_counter_clockwise();
//after that point, loop_to_seam can be modified by 'paths', so don't use it anymore
-
+#if _DEBUG
+ for (auto it = std::next(loop.paths.begin()); it != loop.paths.end(); ++it) {
+ assert(it->polyline.points.size() >= 2);
+ assert(std::prev(it)->polyline.last_point() == it->polyline.first_point());
+ }
+ assert(loop.paths.front().first_point() == loop.paths.back().last_point());
+#endif
// clip the path to avoid the extruder to get exactly on the first point of the loop;
// if polyline was shorter than the clipping distance we'd get a null polyline, so
// we discard it in that case
@@ -3978,8 +3984,8 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
// reset acceleration
m_writer.set_acceleration((uint16_t)floor(get_default_acceleration(m_config) + 0.5));
- if (m_wipe.enable)
- m_wipe.path = paths.front().polyline; // TODO: don't limit wipe to last path
+ //basic wipe, may be erased after if we need a more complex one
+ add_wipe_points(paths);
//wipe for External Perimeter (and not vase)
if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 0 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 2
@@ -4159,16 +4165,39 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
return gcode;
}
+template <typename THING>
+void GCode::add_wipe_points(const std::vector<THING>& paths) {
+ if (m_wipe.enable) {
+ m_wipe.path = std::move(paths.back().polyline);
+ m_wipe.path.reverse();
+
+ for (auto it = std::next(paths.rbegin()); it != paths.rend(); ++it) {
+ if (is_bridge(it->role()))
+ break; // Do not perform a wipe on bridges.
+
+ assert(it->polyline.points.size() >= 2);
+ assert(m_wipe.path.points.back() == it->polyline.last_point());
+ if (m_wipe.path.points.back() != it->polyline.last_point())
+ break; // ExtrusionMultiPath is interrupted in some place.
+
+ m_wipe.path.points.insert(m_wipe.path.points.end(), it->polyline.points.rbegin() + 1, it->polyline.points.rend());
+ }
+ }
+}
+
std::string GCode::extrude_multi_path(const ExtrusionMultiPath &multipath, const std::string &description, double speed) {
+#if _DEBUG
+ for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++it) {
+ assert(it->polyline.points.size() >= 2);
+ assert(std::prev(it)->polyline.last_point() == it->polyline.first_point());
+ }
+#endif
// extrude along the path
std::string gcode;
for (const ExtrusionPath &path : multipath.paths) {
gcode += extrude_path(path, description, speed);
}
- if (m_wipe.enable) {
- m_wipe.path = std::move(multipath.paths.back().polyline); // TODO: don't limit wipe to last path
- m_wipe.path.reverse();
- }
+ add_wipe_points(multipath.paths);
// reset acceleration
m_writer.set_acceleration((uint16_t)floor(get_default_acceleration(m_config) + 0.5));
return gcode;
@@ -4202,10 +4231,7 @@ std::string GCode::extrude_multi_path3D(const ExtrusionMultiPath3D &multipath3D,
}
gcode += this->_after_extrude(path);
}
- if (m_wipe.enable) {
- m_wipe.path = std::move(multipath3D.paths.back().polyline); // TODO: don't limit wipe to last path
- m_wipe.path.reverse();
- }
+ add_wipe_points(multipath3D.paths);
// reset acceleration
m_writer.set_acceleration((uint16_t)floor(get_default_acceleration(m_config) + 0.5));
return gcode;
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index a2555ed77..369b96d36 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -302,6 +302,8 @@ private:
std::string extrude_path(const ExtrusionPath &path, const std::string &description, double speed = -1.);
std::string extrude_path_3D(const ExtrusionPath3D &path, const std::string &description, double speed = -1.);
void split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid, bool was_clockwise);
+ template <typename THING = ExtrusionEntity> // can be templated safely because private
+ void add_wipe_points(const std::vector<THING>& paths);
// Extruding multiple objects with soluble / non-soluble / combined supports
// on a multi-material printer, trying to minimize tool switches.
diff --git a/src/libslic3r/Geometry/MedialAxis.cpp b/src/libslic3r/Geometry/MedialAxis.cpp
index 16a710a48..5c758c627 100644
--- a/src/libslic3r/Geometry/MedialAxis.cpp
+++ b/src/libslic3r/Geometry/MedialAxis.cpp
@@ -3043,8 +3043,11 @@ MedialAxis::build(ThickPolylines& polylines_out)
}
+ExtrusionMultiPath variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance) {
+ return ExtrusionMultiPath(unsafe_variable_width(polyline, role, flow, resolution_internal, tolerance));
+}
ExtrusionPaths
-variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance)
+unsafe_variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance)
{
ExtrusionPaths paths;
@@ -3189,7 +3192,7 @@ variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Fl
path.polyline.append(line.b);
} else {
// we need to initialize a new line
- paths.emplace_back(std::move(path));
+ paths.push_back(path);
path = ExtrusionPath(role);
if (wanted_width != current_flow.width()) {
current_flow = current_flow.with_width(wanted_width);
@@ -3207,7 +3210,7 @@ variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Fl
assert(path.polyline.points.size() > 2 || path.first_point() != path.last_point());
}
if (path.polyline.is_valid())
- paths.emplace_back(std::move(path));
+ paths.push_back(path);
return paths;
}
@@ -3223,36 +3226,34 @@ ExtrusionEntitiesPtr
const coord_t tolerance = flow.scaled_width() / 10;//scale_(0.05);
ExtrusionEntitiesPtr coll;
for (const ThickPolyline& p : polylines) {
-
- ExtrusionPaths paths = variable_width(p, role, flow, resolution_internal, tolerance);
+ ExtrusionMultiPath multi_paths = variable_width(p, role, flow, resolution_internal, tolerance);
// Append paths to collection.
- if (!paths.empty()) {
- for (auto it = std::next(paths.begin()); it != paths.end(); ++it) {
+ if (!multi_paths.empty()) {
+#if _DEBUG
+ for (auto it = std::next(multi_paths.paths.begin()); it != multi_paths.paths.end(); ++it) {
assert(it->polyline.points.size() >= 2);
assert(std::prev(it)->polyline.last_point() == it->polyline.first_point());
}
- if (paths.front().first_point().coincides_with_epsilon(paths.back().last_point())) {
- coll.push_back(new ExtrusionLoop(std::move(paths)));
+#endif
+ if (multi_paths.paths.front().first_point().coincides_with_epsilon(multi_paths.paths.back().last_point())) {
+ coll.push_back(new ExtrusionLoop(std::move(multi_paths.paths)));
} else {
if (role == erThinWall) {
//thin walls : avoid to cut them, please.
//also, keep the start, as the start should be already in a frontier where possible.
- ExtrusionEntityCollection* unsortable_coll = new ExtrusionEntityCollection(std::move(paths));
+ ExtrusionEntityCollection* unsortable_coll = new ExtrusionEntityCollection(std::move(multi_paths.paths));
unsortable_coll->set_can_sort_reverse(false, false);
+ //TODO un-reversable multipath ?
coll.push_back(unsortable_coll);
} else if (role == erGapFill) {
- if (paths.size() == 1) {
- coll.push_back(paths.front().clone_move());
+ if (multi_paths.size() == 1) {
+ coll.push_back(multi_paths.paths.front().clone_move());
} else {
- ExtrusionEntityCollection* unsortable_coll = new ExtrusionEntityCollection(std::move(paths));
- //gap fill : can reverse, but refrain from cutting them as it creates a mess.
- // I say that, but currently (false, true) does bad things.
- unsortable_coll->set_can_sort_reverse(false, true);
- coll.push_back(unsortable_coll);
+ //can reverse but not sort/cut: it's a multipath!
+ coll.push_back(multi_paths.clone_move());
}
} else {
- for (ExtrusionPath& path : paths)
- coll.push_back(new ExtrusionPath(std::move(path)));
+ coll.push_back(multi_paths.clone_move());
}
}
}
diff --git a/src/libslic3r/Geometry/MedialAxis.hpp b/src/libslic3r/Geometry/MedialAxis.hpp
index 852c0161c..c010bc14f 100644
--- a/src/libslic3r/Geometry/MedialAxis.hpp
+++ b/src/libslic3r/Geometry/MedialAxis.hpp
@@ -159,7 +159,9 @@ private:
/// create a ExtrusionEntitiesPtr from ThickPolylines, discretizing the variable width into little sections (of 4*SCALED_RESOLUTION length) where needed. Please delete all ptr if not used.
ExtrusionEntitiesPtr thin_variable_width(const ThickPolylines& polylines, const ExtrusionRole role, const Flow &flow, const coord_t resolution_internal);
// used by thin_variable_width. Only does the work for a single polyline.
-ExtrusionPaths variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance);
+ExtrusionMultiPath variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance);
+//prefer using multi_variable_width
+ExtrusionPaths unsafe_variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance);
} } // namespace Slicer::Geometry
diff --git a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp
index caaf1ee9c..062a3b397 100644
--- a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp
+++ b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp
@@ -3,6 +3,7 @@
#include <CGAL/Surface_sweep_2_algorithms.h>
#include "libslic3r/Geometry/Voronoi.hpp"
+#include "libslic3r/Arachne/utils/VoronoiUtils.hpp"
#include "VoronoiUtilsCgal.hpp"
@@ -28,7 +29,8 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_
if (edge.color() != 0)
continue;
- if (edge.is_finite() && edge.is_linear()) {
+ if (edge.is_finite() && edge.is_linear() && edge.vertex0() != nullptr && edge.vertex1() != nullptr &&
+ Arachne::VoronoiUtils::is_finite(*edge.vertex0()) && Arachne::VoronoiUtils::is_finite(*edge.vertex1())) {
segments.emplace_back(to_cgal_point(*edge.vertex0()), to_cgal_point(*edge.vertex1()));
edge.color(1);
assert(edge.twin() != nullptr);
@@ -73,7 +75,8 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &vor
do {
// FIXME Lukas H.: Also process parabolic segments.
- if (edge->is_finite() && edge->is_linear())
+ if (edge->is_finite() && edge->is_linear() && edge->vertex0() != nullptr && edge->vertex1() != nullptr &&
+ Arachne::VoronoiUtils::is_finite(*edge->vertex0()) && Arachne::VoronoiUtils::is_finite(*edge->vertex1()))
edges.emplace_back(edge);
edge = edge->rot_next();
diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index 7fbadc32a..47f2923f0 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -29,6 +29,13 @@
#include <list>
#include <boost/log/trivial.hpp>
+//#define ARACHNE_DEBUG
+
+#ifdef ARACHNE_DEBUG
+#include "SVG.hpp"
+#include "Utils.hpp"
+#endif
+
namespace Slic3r {
PerimeterGeneratorLoops get_all_Childs(PerimeterGeneratorLoop loop) {
@@ -119,7 +126,7 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine &ext_lines, double fuzzy
}
if (ext_lines.back().p == ext_lines.front().p) // Connect endpoints.
- out.back().p = out.front().p;
+ out.front().p = out.back().p;
if (out.size() >= 3)
ext_lines.junctions = std::move(out);
@@ -137,6 +144,27 @@ void convert_to_clipperpath(const Polygons& source, ClipperLib_Z::Paths& dest) {
}
}
+#ifdef ARACHNE_DEBUG
+static void export_perimeters_to_svg(const std::string &path, const Polygons &contours, const std::vector<Arachne::VariableWidthLines> &perimeters, const ExPolygons &infill_area)
+{
+ coordf_t stroke_width = scale_(0.03);
+ BoundingBox bbox = get_extents(contours);
+ bbox.offset(scale_(1.));
+ ::Slic3r::SVG svg(path.c_str(), bbox);
+
+ svg.draw(infill_area, "cyan");
+
+ for (const Arachne::VariableWidthLines &perimeter : perimeters)
+ for (const Arachne::ExtrusionLine &extrusion_line : perimeter) {
+ ThickPolyline thick_polyline = to_thick_polyline(extrusion_line);
+ svg.draw({thick_polyline}, "green", "blue", stroke_width);
+ }
+
+ for (const Line &line : to_lines(contours))
+ svg.draw(line, "red", stroke_width);
+}
+#endif
+
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const Surface& surface) {
@@ -167,6 +195,13 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
loop_number = int(perimeters.size()) - 1;
+#ifdef ARACHNE_DEBUG
+ {
+ static int iRun = 0;
+ export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour()));
+ }
+#endif
+
// All closed ExtrusionLine should have the same the first and the last point.
// But in rare cases, Arachne produce ExtrusionLine marked as closed but without
// equal the first and the last point.
@@ -1907,7 +1942,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const Polyline& loop_polygon
return paths;
}
-
+//TODO: transform to ExtrusionMultiPath instead of ExtrusionPaths
ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& arachne_path, ExtrusionRole role, bool is_external) const {
ExtrusionPaths paths;
const bool is_loop = Point{ arachne_path.front().x(), arachne_path.front().y() }.coincides_with_epsilon(Point{ arachne_path.back().x(), arachne_path.back().y() });
@@ -1918,7 +1953,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar
//assert(path.mm3_per_mm == path.mm3_per_mm);
//assert(path.width == path.width);
//assert(path.height == path.height);
- append(paths, Geometry::variable_width(Arachne::to_thick_polyline(arachne_path),
+ append(paths, Geometry::unsafe_variable_width(Arachne::to_thick_polyline(arachne_path),
role,
is_external ? this->ext_perimeter_flow : this->perimeter_flow,
std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)),
@@ -2020,14 +2055,14 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar
if (!ok_polylines.empty()) {
//fast track
if (small_speed.empty() && big_speed.empty() && small_flow.empty() && big_flow.empty()) {
- return Geometry::variable_width(Arachne::to_thick_polyline(arachne_path),
+ return Geometry::unsafe_variable_width(Arachne::to_thick_polyline(arachne_path),
role,
is_external ? this->ext_perimeter_flow : this->perimeter_flow,
std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)),
(is_external ? this->ext_perimeter_flow : this->perimeter_flow).scaled_width() / 10);
}
for (const ClipperLib_Z::Path& extrusion_path : ok_polylines) {
- for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path),
+ for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path),
role,
is_external ? this->ext_perimeter_flow : this->perimeter_flow,
std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)),
@@ -2039,7 +2074,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar
}
if (!small_speed.empty()) {
for (const ClipperLib_Z::Path& extrusion_path : small_speed) {
- for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path),
+ for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path),
erOverhangPerimeter,
is_external ? this->ext_perimeter_flow : this->perimeter_flow,
std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)),
@@ -2051,7 +2086,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar
}
if (!big_speed.empty()) {
for (const ClipperLib_Z::Path& extrusion_path : big_speed) {
- for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path),
+ for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path),
erOverhangPerimeter,
is_external ? this->ext_perimeter_flow : this->perimeter_flow,
std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)),
@@ -2063,7 +2098,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar
}
if (!small_flow.empty()) {
for (const ClipperLib_Z::Path& extrusion_path : small_flow) {
- for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path),
+ for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path),
erOverhangPerimeter,
this->overhang_flow,
std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)),
@@ -2075,7 +2110,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar
}
if (!big_flow.empty()) {
for (const ClipperLib_Z::Path& extrusion_path : big_flow) {
- for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path),
+ for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path),
erOverhangPerimeter,
this->overhang_flow,
std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)),
@@ -2540,7 +2575,7 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_extrusions(std::vector<P
chain_and_reorder_extrusion_paths(paths, &start_point);
}
} else {
- append(paths, Geometry::variable_width(Arachne::to_thick_polyline(*extrusion),
+ append(paths, Geometry::unsafe_variable_width(Arachne::to_thick_polyline(*extrusion),
role,
is_external ? this->ext_perimeter_flow : this->perimeter_flow,
std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)),
@@ -2565,11 +2600,37 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_extrusions(std::vector<P
extrusion_loop.make_counter_clockwise();
else
extrusion_loop.make_clockwise();
-
+#if _DEBUG
+ for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) {
+ assert(it->polyline.points.size() >= 2);
+ assert(std::prev(it)->polyline.last_point() == it->polyline.first_point());
+ }
+ assert(extrusion_loop.paths.front().first_point() == extrusion_loop.paths.back().last_point());
+#endif
extrusion_coll.append(std::move(extrusion_loop));
- } else
- for (ExtrusionPath& path : paths)
- extrusion_coll.append(ExtrusionPath(std::move(path)));
+ } else {
+
+ // Because we are processing one ExtrusionLine all ExtrusionPaths should form one connected path.
+ // But there is possibility that due to numerical issue there is poss
+ assert([&paths = std::as_const(paths)]() -> bool {
+ for (auto it = std::next(paths.begin()); it != paths.end(); ++it)
+ if (std::prev(it)->polyline.last_point() != it->polyline.first_point())
+ return false;
+ return true;
+ }());
+ ExtrusionMultiPath multi_path;
+ multi_path.paths.emplace_back(std::move(paths.front()));
+
+ for (auto it_path = std::next(paths.begin()); it_path != paths.end(); ++it_path) {
+ if (multi_path.paths.back().last_point() != it_path->first_point()) {
+ extrusion_coll.append(std::move(multi_path));
+ multi_path = ExtrusionMultiPath();
+ }
+ multi_path.paths.push_back(std::move(*it_path));
+ }
+
+ extrusion_coll.append(ExtrusionMultiPath(std::move(multi_path)));
+ }
}
}
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index d7f3617ac..b1389a953 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -6000,7 +6000,8 @@ void PrintConfigDef::init_fff_params()
def->category = OptionCategory::perimeter;
def->tooltip = L("Classic perimeter generator produces perimeters with constant extrusion width and for "
"very thin areas is used gap-fill. "
- "Arachne engine produces perimeters with variable extrusion width.");
+ "Arachne engine produces perimeters with variable extrusion width. "
+ "This setting also affects the Concentric infill.");
def->enum_keys_map = &ConfigOptionEnum<PerimeterGeneratorType>::get_enum_values();
def->enum_values.push_back("classic");
def->enum_values.push_back("arachne");
diff --git a/src/libslic3r/SVG.cpp b/src/libslic3r/SVG.cpp
index 45eb20d94..893cbdd95 100644
--- a/src/libslic3r/SVG.cpp
+++ b/src/libslic3r/SVG.cpp
@@ -308,26 +308,29 @@ std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool clo
return d.str();
}
-void SVG::draw_text(const Point &pt, const char *text, const char *color)
+void SVG::draw_text(const Point &pt, const char *text, const char *color, const coordf_t font_size)
{
fprintf(this->f,
- "<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"20px\" fill=\"%s\">%s</text>",
- to_svg_x(pt(0)-origin(0)),
- to_svg_y(pt(1)-origin(1)),
+ R"(<text x="%f" y="%f" font-family="sans-serif" font-size="%fpx" fill="%s">%s</text>)",
+ to_svg_x(float(pt.x() - origin.x())),
+ to_svg_y(float(pt.y() - origin.y())),
+ font_size,
color, text);
}
-void SVG::draw_legend(const Point &pt, const char *text, const char *color)
+void SVG::draw_legend(const Point &pt, const char *text, const char *color, const coordf_t font_size)
{
fprintf(this->f,
- "<circle cx=\"%f\" cy=\"%f\" r=\"10\" fill=\"%s\"/>",
- to_svg_x(pt(0)-origin(0)),
- to_svg_y(pt(1)-origin(1)),
+ R"(<circle cx="%f" cy="%f" r="%f" fill="%s"/>)",
+ to_svg_x(float(pt.x() - origin.x())),
+ to_svg_y(float(pt.y() - origin.y())),
+ font_size,
color);
fprintf(this->f,
- "<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"10px\" fill=\"%s\">%s</text>",
- to_svg_x(pt(0)-origin(0)) + 20.f,
- to_svg_y(pt(1)-origin(1)),
+ R"(<text x="%f" y="%f" font-family="sans-serif" font-size="%fpx" fill="%s">%s</text>)",
+ to_svg_x(float(pt.x() - origin.x())) + 20.f,
+ to_svg_y(float(pt.y() - origin.y())),
+ font_size,
"black", text);
}
diff --git a/src/libslic3r/SVG.hpp b/src/libslic3r/SVG.hpp
index 617cd9679..5e2d92507 100644
--- a/src/libslic3r/SVG.hpp
+++ b/src/libslic3r/SVG.hpp
@@ -76,8 +76,8 @@ public:
void draw(const ClipperLib::Path &polygon, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
void draw(const ClipperLib::Paths &polygons, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
- void draw_text(const Point &pt, const char *text, const char *color);
- void draw_legend(const Point &pt, const char *text, const char *color);
+ void draw_text(const Point &pt, const char *text, const char *color, coordf_t font_size = 20.f);
+ void draw_legend(const Point &pt, const char *text, const char *color, coordf_t font_size = 10.f);
void Close();
diff --git a/tests/libslic3r/test_arachne.cpp b/tests/libslic3r/test_arachne.cpp
index 991fae00e..7ef5eda14 100644
--- a/tests/libslic3r/test_arachne.cpp
+++ b/tests/libslic3r/test_arachne.cpp
@@ -33,30 +33,19 @@ static void draw_extrusion(const std::string &path, const Polygons &polygons, co
TEST_CASE("Arachne - Closed ExtrusionLine", "[ArachneClosedExtrusionLine]") {
Polygon poly = {
- Point(62478540, -7411873), Point(62478540, 9978540), Point(-62478540, 9978540), Point(-62478540, -7411873), Point(-58818049, -7411874),
- Point(-58639424, -7393054), Point(-58430204, -7325743), Point(-58317958, -7261069), Point(-58187096, -7150294), Point(-58032997, -6934055),
- Point(-57956770, -6723830), Point(-57927922, -6536131), Point(-57948215, -6249353), Point(-58038066, -5971432), Point(-58400146, -5419566),
- Point(-58720844, -4711417), Point(-58812247, -4429032), Point(-58945877, -3868129), Point(-58999054, -3458536), Point(-59021495, -3000104),
- Point(-58978088, -2345755), Point(-58945641, -2130557), Point(-58719408, -1284462), Point(-58350699, -492550), Point(-57854519, 218384),
- Point(-57690839, 403070), Point(-57242241, 834472), Point(-56937894, 1068372), Point(-56522699, 1341801), Point(-56245930, 1488656),
- Point(-55633586, 1748152), Point(-54872819, 1945077), Point(-54279560, 2011550), Point(-53999789, 2021482), Point(-53550613, 1995780),
- Point(-53127364, 1945077), Point(-52852060, 1886312), Point(-52262142, 1711199), Point(-51479386, 1343079), Point(-50763215, 839141),
- Point(-50302640, 384003), Point(-50150730, 224721), Point(-49616021, -551391), Point(-49279548, -1287513), Point(-49210805, -1492871),
- Point(-49054178, -2131559), Point(-49004097, -2510916), Point(-48978506, -2999863), Point(-49012690, -3563440), Point(-49054263, -3868963),
- Point(-49280289, -4714703), Point(-49649307, -5507428), Point(-49921685, -5909300), Point(-49982145, -6037568), Point(-50058406, -6311890),
- Point(-50071436, -6450122), Point(-50045218, -6724784), Point(-50006267, -6852153), Point(-49876677, -7082551), Point(-49687434, -7260679),
- Point(-49451449, -7373245), Point(-49184639, -7411873), Point(-13258854, -7411871), Point(-13258853, -7021460), Point(-44501672, -7021460),
- Point(-44816622, -6971390), Point(-45100558, -6826592), Point(-45326629, -6600516), Point(-45471725, -6315721), Point(-45521460, -6001689),
- Point(-45521460, 3000836), Point(-45509082, 3159381), Point(-45412385, 3459576), Point(-45326445, 3600421), Point(-45221777, 3722975),
- Point(-44964550, 3909861), Point(-44815338, 3971666), Point(-44598502, 4016264), Point(58501687, 4021460), Point(58814884, 3971856),
- Point(58964551, 3909863), Point(59221777, 3722976), Point(59326668, 3600164), Point(59436707, 3406438), Point(59508907, 3160338),
- Point(59521460, 3000842), Point(59521460, -6001688), Point(59471724, -6315713), Point(59326597, -6600557), Point(59100555, -6826595),
- Point(58814822, -6971976), Point(58501662, -7021460), Point(27258850, -7021460), Point(27258851, -7411871), Point(59755385, -7411874),
+ Point(-40000000, 10000000),
+ Point(-62480000, 10000000),
+ Point(-62480000, -7410000),
+ Point(-58430000, -7330000),
+ Point(-58400000, -5420000),
+ Point(-58720000, -4710000),
+ Point(-58940000, -3870000),
+ Point(-59020000, -3000000),
};
Polygons polygons = {poly};
coord_t spacing = 407079;
- coord_t inset_count = 8;
+ coord_t inset_count = 5;
Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, PrintObjectConfig::defaults(), PrintConfig::defaults());
wallToolPaths.generate();
@@ -69,7 +58,7 @@ TEST_CASE("Arachne - Closed ExtrusionLine", "[ArachneClosedExtrusionLine]") {
for (VariableWidthLines &perimeter : perimeters)
for (ExtrusionLine &el : perimeter)
if (el.is_closed) {
-// REQUIRE(el.junctions.front().p == el.junctions.back().p);
+ REQUIRE(el.junctions.front().p == el.junctions.back().p);
}
}
@@ -102,3 +91,325 @@ TEST_CASE("Arachne - Missing perimeter - #8472", "[ArachneMissingPerimeter8472]"
REQUIRE(perimeters.size() == 3);
}
+// This test case was distilled from GitHub issue #8593.
+// Where on the symmetrical model, there were missing parts of extrusions in gear teeth based on model rotation.
+TEST_CASE("Arachne - #8593 - Missing a part of the extrusion", "[ArachneMissingPartOfExtrusion8593]") {
+ const Polygon poly_orig = {
+ Point( 1800000, 28500000),
+ Point( 1100000, 30000000),
+ Point( 1000000, 30900000),
+ Point( 600000, 32300000),
+ Point( -600000, 32300000),
+ Point(-1000000, 30900000),
+ Point(-1100000, 30000000),
+ Point(-1800000, 29000000),
+ };
+
+ coord_t spacing = 377079;
+ coord_t inset_count = 3;
+
+ PrintObjectConfig print_object_config = PrintObjectConfig::defaults();
+ print_object_config.min_bead_width.set(new ConfigOptionFloatOrPercent(0.315, false));
+ print_object_config.wall_transition_angle.set(new ConfigOptionFloat(40.));
+ print_object_config.wall_transition_length.set(new ConfigOptionFloatOrPercent(1., false));
+
+ // This behavior seems to be related to the rotation of the input polygon.
+ // There are specific angles in which this behavior is always triggered.
+ for (const double angle : {0., -PI / 2., -PI / 15.}) {
+ Polygon poly = poly_orig;
+ if (angle != 0.)
+ poly.rotate(angle);
+
+ Polygons polygons = {poly};
+ Arachne::WallToolPaths wall_tool_paths(polygons, spacing, spacing, inset_count, 0, 0.2, print_object_config, PrintConfig::defaults());
+ wall_tool_paths.generate();
+ std::vector<Arachne::VariableWidthLines> perimeters = wall_tool_paths.getToolPaths();
+
+#ifdef ARACHNE_DEBUG_OUT
+ {
+ static int iRun = 0;
+ export_perimeters_to_svg(debug_out_path("arachne-missing-part-of-extrusion-8593-%d.svg", iRun++), polygons, perimeters, union_ex(wall_tool_paths.getInnerContour()));
+ }
+#endif
+ }
+}
+
+// This test case was distilled from GitHub issue #8573.
+TEST_CASE("Arachne - #8573 - A gap in the perimeter - 1", "[ArachneGapInPerimeter8573_1]") {
+ const Polygon poly = {
+ Point(13960000, 500000),
+ Point(13920000, 1210000),
+ Point(13490000, 2270000),
+ Point(12960000, 3400000),
+ Point(12470000, 4320000),
+ Point(12160000, 4630000),
+ Point(12460000, 3780000),
+ Point(12700000, 2850000),
+ Point(12880000, 1910000),
+ Point(12950000, 1270000),
+ Point(13000000, 500000),
+ };
+
+ Polygons polygons = {poly};
+ coord_t spacing = 407079;
+ coord_t inset_count = 2;
+
+ PrintObjectConfig print_object_config = PrintObjectConfig::defaults();
+// print_object_config.wall_transition_angle.set(new ConfigOptionFloat(20.));
+
+ Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, print_object_config, PrintConfig::defaults());
+ wallToolPaths.generate();
+ std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
+
+#ifdef ARACHNE_DEBUG_OUT
+ export_perimeters_to_svg(debug_out_path("arachne-gap-in-perimeter-1-8573.svg"), polygons, perimeters, union_ex(wallToolPaths.getInnerContour()));
+#endif
+}
+
+// This test case was distilled from GitHub issue #8444.
+TEST_CASE("Arachne - #8444 - A gap in the perimeter - 2", "[ArachneGapInPerimeter8444_2]") {
+ const Polygon poly = {
+ Point(14413938, 3825902),
+ Point(16817613, 711749),
+ Point(19653030, 67154),
+ Point(20075592, 925370),
+ Point(20245428, 1339788),
+ Point(20493219, 2121894),
+ Point(20570295, 2486625),
+ Point(20616559, 2835232),
+ Point(20631964, 3166882),
+ Point(20591800, 3858877),
+ Point(19928267, 2153012),
+ Point(19723020, 1829802),
+ Point(19482017, 1612364),
+ Point(19344810, 1542433),
+ Point(19200249, 1500902),
+ Point(19047680, 1487200),
+ Point(18631073, 1520777),
+ Point(18377524, 1567627),
+ Point(18132517, 1641174),
+ Point(17896307, 1741360),
+ Point(17669042, 1868075),
+ Point(17449999, 2021790),
+ };
+
+ Polygons polygons = {poly};
+ coord_t spacing = 594159;
+ coord_t inset_count = 2;
+
+ PrintObjectConfig print_object_config = PrintObjectConfig::defaults();
+ // print_object_config.wall_transition_angle.set(new ConfigOptionFloat(20.));
+
+ Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.4, print_object_config, PrintConfig::defaults());
+ wallToolPaths.generate();
+ std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
+
+#ifdef ARACHNE_DEBUG_OUT
+ export_perimeters_to_svg(debug_out_path("arachne-gap-in-perimeter-2-8444.svg"), polygons, perimeters, union_ex(wallToolPaths.getInnerContour()));
+#endif
+}
+
+// This test case was distilled from GitHub issue #8528.
+// There is a hole in the place where the number of perimeters is changing from 6 perimeters to 7 perimeters.
+TEST_CASE("Arachne - #8528 - A hole when number of perimeters is changing", "[ArachneHoleOnPerimetersChange8528]") {
+ const Polygon poly = {
+ Point(-30000000, 27650000),
+ Point(-30000000, 33500000),
+ Point(-40000000, 33500000),
+ Point(-40500000, 33500000),
+ Point(-41100000, 33400000),
+ Point(-41600000, 33200000),
+ Point(-42100000, 32900000),
+ Point(-42600000, 32600000),
+ Point(-43000000, 32200000),
+ Point(-43300000, 31700000),
+ Point(-43600000, 31200000),
+ Point(-43800000, 30700000),
+ Point(-43900000, 30100000),
+ Point(-43900000, 29600000),
+ Point(-43957080, 25000000),
+ Point(-39042920, 25000000),
+ Point(-39042920, 27650000),
+ };
+
+ Polygons polygons = {poly};
+ coord_t spacing = 814159;
+ coord_t inset_count = 5;
+
+ PrintObjectConfig print_object_config = PrintObjectConfig::defaults();
+ print_object_config.min_bead_width.set(new ConfigOptionFloatOrPercent(0.68, false));
+
+ // Changing min_bead_width to 0.66 seems that resolve this issue, at least in this case.
+ print_object_config.min_bead_width.set(new ConfigOptionFloatOrPercent(0.66, false));
+
+ Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.4, print_object_config, PrintConfig::defaults());
+ wallToolPaths.generate();
+ std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
+
+#ifdef ARACHNE_DEBUG_OUT
+ export_perimeters_to_svg(debug_out_path("arachne-hole-on-perimeters-change-8528.svg"), polygons, perimeters, union_ex(wallToolPaths.getInnerContour()));
+#endif
+}
+
+// This test case was distilled from GitHub issue #8528.
+// There is an inconsistency between layers in length of the single perimeters.
+TEST_CASE("Arachne - #8555 - Inconsistent single perimeter", "[ArachneInconsistentSinglePerimeter8555]") {
+ const Polygon poly_0 = {
+ Point(5527411, -38490007),
+ Point(11118814, -36631169),
+ Point(13529600, -36167120),
+ Point(11300145, -36114514),
+ Point(10484024, -36113916),
+ Point(5037323, -37985945),
+ Point(4097054, -39978866)
+ };
+ const Polygon poly_1 = {
+ Point(5566841, -38517205),
+ Point(11185208, -36649404),
+ Point(13462719, -36211009),
+ Point(11357290, -36161329),
+ Point(10583855, -36160763),
+ Point(5105952, -38043516),
+ Point(4222019, -39917031)
+ };
+ const Polygon poly_2 = {
+ Point(5606269, -38544404),
+ Point(11251599, -36667638),
+ Point(13391666, -36255700),
+ Point(10683552, -36207653),
+ Point(5174580, -38101085),
+ Point(4346981, -39855197)
+ };
+ const Polygon poly_3 = {
+ Point(5645699, -38571603),
+ Point(11317993, -36685873),
+ Point(13324786, -36299588),
+ Point(10783383, -36254499),
+ Point(5243209, -38158655),
+ Point(4471947, -39793362)
+ };
+ const Polygon poly_4 = {
+ Point(5685128, -38598801),
+ Point(11384385, -36704108),
+ Point(13257907, -36343476),
+ Point(10883211, -36301345),
+ Point(5311836, -38216224),
+ Point(4596909, -39731528)
+ };
+ const Polygon poly_5 = {
+ Point(5724558, -38626000),
+ Point(11450778, -36722343),
+ Point(13191026, -36387365),
+ Point(10983042, -36348191),
+ Point(5380466, -38273795),
+ Point(4721874, -39669693)
+ };
+
+ Polygons polygons = {poly_0, poly_1, poly_2, poly_3, poly_4, poly_5};
+ coord_t spacing = 417809;
+ coord_t inset_count = 2;
+
+ for (size_t poly_idx = 0; poly_idx < polygons.size(); ++poly_idx) {
+ Polygons input_polygons{polygons[poly_idx]};
+ Arachne::WallToolPaths wallToolPaths(input_polygons, spacing, spacing, inset_count, 0, 0.15, PrintObjectConfig::defaults(), PrintConfig::defaults());
+ wallToolPaths.generate();
+ std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
+
+#ifdef ARACHNE_DEBUG_OUT
+ export_perimeters_to_svg(debug_out_path("arachne-inconsistent-single-perimeter-8555-%d.svg", poly_idx), input_polygons, perimeters, union_ex(wallToolPaths.getInnerContour()));
+#endif
+ }
+}
+
+// This test case was distilled from GitHub issue #8633.
+// Open perimeter extrusion is shorter on endpoints in comparison to closed perimeter.
+TEST_CASE("Arachne - #8633 - Shorter open perimeter", "[ArachneShorterOpenPerimeter8633]") {
+ const Polygon poly_0 = {
+ Point(6507498, 4189461),
+ Point(6460382, 3601960),
+ Point(6390896, 3181097),
+ Point(6294072, 2765838),
+ Point(6170293, 2357794),
+
+ Point(7090581, 2045388),
+ Point(7232821, 2514293),
+ Point(7344089, 2991501),
+ Point(7423910, 3474969),
+ Point(7471937, 3962592),
+ Point(7487443, 4436235),
+ Point(6515575, 4436235),
+ };
+
+ const Polygon poly_1 = {
+ Point(6507498, 4189461),
+ Point(6460382, 3601960),
+ Point(6390896, 3181097),
+ Point(6294072, 2765838),
+ Point(6170293, 2357794),
+
+ Point(6917958, 1586830),
+ Point(7090552, 2045398),
+
+ Point(7232821, 2514293),
+ Point(7344089, 2991501),
+ Point(7423910, 3474969),
+ Point(7471937, 3962592),
+ Point(7487443, 4436235),
+ Point(6515575, 4436235),
+ };
+
+ Polygons polygons = {poly_0, poly_1};
+ coord_t spacing = 617809;
+ coord_t inset_count = 1;
+
+ PrintObjectConfig print_object_config = PrintObjectConfig::defaults();
+ print_object_config.min_bead_width.set(new ConfigOptionFloatOrPercent(0.51, false));
+ print_object_config.min_feature_size.set(new ConfigOptionFloatOrPercent(0.15, false));
+ print_object_config.wall_transition_length.set(new ConfigOptionFloatOrPercent(0.6, false));
+
+ for (size_t poly_idx = 0; poly_idx < polygons.size(); ++poly_idx) {
+ Polygons input_polygons{polygons[poly_idx]};
+ Arachne::WallToolPaths wallToolPaths(input_polygons, spacing, spacing, inset_count, 0, 0.15, print_object_config, PrintConfig::defaults());
+ wallToolPaths.generate();
+ std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
+
+#ifdef ARACHNE_DEBUG_OUT
+ export_perimeters_to_svg(debug_out_path("arachne-shorter-open-perimeter-8633-%d.svg", poly_idx), input_polygons, perimeters, union_ex(wallToolPaths.getInnerContour()));
+#endif
+ }
+}
+
+// This test case was distilled from GitHub issue #8597.
+// There was just an issue with decrementing std::vector::begin() in a specific case.
+TEST_CASE("Arachne - #8597 - removeSmallAreas", "[ArachneRemoveSmallAreas8597]") {
+ const Polygon poly_0 = {
+ Point(-38768167, -3636556),
+ Point(-38763631, -3617883),
+ Point(-38763925, -3617820),
+ Point(-38990169, -3919539),
+ Point(-38928506, -3919539),
+ };
+
+ const Polygon poly_1 = {
+ Point(-39521732, -4480560),
+ Point(-39383333, -4398498),
+ Point(-39119825, -3925307),
+ Point(-39165608, -3926212),
+ Point(-39302205, -3959445),
+ Point(-39578719, -4537002),
+ };
+
+ Polygons polygons = {poly_0, poly_1};
+ coord_t spacing = 407079;
+ coord_t inset_count = 2;
+
+ Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, PrintObjectConfig::defaults(), PrintConfig::defaults());
+ wallToolPaths.generate();
+ std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
+
+#ifdef ARACHNE_DEBUG_OUT
+ export_perimeters_to_svg(debug_out_path("arachne-remove-small-areas-8597.svg"), polygons, perimeters, union_ex(wallToolPaths.getInnerContour()));
+#endif
+
+ REQUIRE(perimeters.size() == 1);
+} \ No newline at end of file