From dc2789a0e81030f6ac33127556f311c2dcf6e533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 9 Jun 2022 10:12:30 +0200 Subject: [PATCH] Added computation of bridging perimeters for Arachne. --- src/libslic3r/Arachne/utils/ExtrusionLine.cpp | 17 +++ src/libslic3r/Arachne/utils/ExtrusionLine.hpp | 31 +++++ src/libslic3r/PerimeterGenerator.cpp | 121 +++++++++++++++--- 3 files changed, 148 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Arachne/utils/ExtrusionLine.cpp b/src/libslic3r/Arachne/utils/ExtrusionLine.cpp index bfec48075..0b9c9a7b1 100644 --- a/src/libslic3r/Arachne/utils/ExtrusionLine.cpp +++ b/src/libslic3r/Arachne/utils/ExtrusionLine.cpp @@ -5,6 +5,7 @@ #include "ExtrusionLine.hpp" #include "linearAlg2D.hpp" +#include "../../PerimeterGenerator.hpp" namespace Slic3r::Arachne { @@ -231,4 +232,20 @@ int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, } } +} // namespace Slic3r::Arachne + +namespace Slic3r { +void extrusion_paths_append(ExtrusionPaths &dst, const ClipperLib_Z::Paths &extrusion_paths, const ExtrusionRole role, const Flow &flow) +{ + for (const ClipperLib_Z::Path &extrusion_path : extrusion_paths) { + ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion_path); + Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled(0.05), 0)); + } } + +void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow) +{ + ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion); + Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled(0.05), 0)); +} +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/Arachne/utils/ExtrusionLine.hpp b/src/libslic3r/Arachne/utils/ExtrusionLine.hpp index 07d8d41a2..a7189cebd 100644 --- a/src/libslic3r/Arachne/utils/ExtrusionLine.hpp +++ b/src/libslic3r/Arachne/utils/ExtrusionLine.hpp @@ -9,6 +9,9 @@ #include "../../Polyline.hpp" #include "../../Polygon.hpp" #include "../../BoundingBox.hpp" +#include "../../ExtrusionEntity.hpp" +#include "../../Flow.hpp" +#include "../../../clipper/clipper_z.hpp" namespace Slic3r { class ThickPolyline; @@ -208,6 +211,26 @@ static inline Slic3r::ThickPolyline to_thick_polyline(const Arachne::ExtrusionLi return out; } +static inline Slic3r::ThickPolyline to_thick_polyline(const ClipperLib_Z::Path &path) +{ + assert(path.size() >= 2); + Slic3r::ThickPolyline out; + out.points.emplace_back(path.front().x(), path.front().y()); + out.width.emplace_back(path.front().z()); + out.points.emplace_back(path[1].x(), path[1].y()); + out.width.emplace_back(path[1].z()); + + auto it_prev = path.begin() + 1; + for (auto it = path.begin() + 2; it != path.end(); ++it) { + out.points.emplace_back(it->x(), it->y()); + out.width.emplace_back(it_prev->z()); + out.width.emplace_back(it->z()); + it_prev = it; + } + + return out; +} + static inline Polygon to_polygon(const ExtrusionLine &line) { Polygon out; @@ -269,4 +292,12 @@ static std::vector to_points(const std::vector &e using VariableWidthLines = std::vector; // #include @@ -277,6 +280,101 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime return out; } +static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path &subject, const ClipperLib_Z::Paths &clip, ClipperLib_Z::ClipType clipType) +{ + 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) { + ClipperLib_Z::IntPoint start = e1bot; + ClipperLib_Z::IntPoint end = e1top; + + if (start.z() <= 0 && end.z() <= 0) { + start = e2bot; + end = e2top; + } + + assert(start.z() > 0 && end.z() > 0); + + // Interpolate extrusion line width. + double length_sqr = (end - start).cast().squaredNorm(); + double dist_sqr = (pt - start).cast().squaredNorm(); + double t = std::sqrt(dist_sqr / length_sqr); + + pt.z() = start.z() + coord_t((end.z() - start.z()) * t); + }); + + clipper.AddPath(subject, ClipperLib_Z::ptSubject, false); + clipper.AddPaths(clip, ClipperLib_Z::ptClip, true); + + ClipperLib_Z::PolyTree clipped_polytree; + ClipperLib_Z::Paths clipped_paths; + clipper.Execute(clipType, clipped_polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero); + ClipperLib_Z::PolyTreeToPaths(clipped_polytree, clipped_paths); + + return clipped_paths; +} + +static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &perimeter_generator, const std::vector &extrusions) +{ + ExtrusionEntityCollection extrusion_coll; + for (const Arachne::ExtrusionLine *extrusion : extrusions) { + if (extrusion->empty()) + continue; + + const bool is_external = extrusion->inset_idx == 0; + ExtrusionRole role = is_external ? erExternalPerimeter : erPerimeter; + + ExtrusionPaths paths; + // detect overhanging/bridging perimeters + if (perimeter_generator.config->overhangs && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers + && ! ((perimeter_generator.object_config->support_material || perimeter_generator.object_config->support_material_enforce_layers > 0) && + perimeter_generator.object_config->support_material_contact_distance.value == 0)) { + + ClipperLib_Z::Path extrusion_path; + extrusion_path.reserve(extrusion->size()); + for (const Arachne::ExtrusionJunction &ej : extrusion->junctions) + extrusion_path.emplace_back(ej.p.x(), ej.p.y(), ej.w); + + ClipperLib_Z::Paths lower_slices_paths; + lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size()); + for (const Polygon &poly : perimeter_generator.lower_slices_polygons()) { + lower_slices_paths.emplace_back(); + ClipperLib_Z::Path &out = lower_slices_paths.back(); + out.reserve(poly.points.size()); + for (const Point &pt : poly.points) + out.emplace_back(pt.x(), pt.y(), 0); + } + + // get non-overhang paths by intersecting this loop with the grown lower slices + extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role, + is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow); + + // get overhang paths by checking what parts of this loop fall + // outside the grown lower slices (thus where the distance between + // the loop centerline and original lower slices is >= half nozzle diameter + extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), erOverhangPerimeter, + perimeter_generator.overhang_flow); + + // Reapply the nearest point search for starting point. + // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. + chain_and_reorder_extrusion_paths(paths, &paths.front().first_point()); + } else { + extrusion_paths_append(paths, *extrusion, role, is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow); + } + + // Append paths to collection. + if (!paths.empty()) { + if (extrusion->is_closed) + extrusion_coll.entities.emplace_back(new ExtrusionLoop(std::move(paths))); + else + for (ExtrusionPath &path : paths) + extrusion_coll.entities.emplace_back(new ExtrusionPath(std::move(path))); + } + } + + return extrusion_coll; +} + // 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" void PerimeterGenerator::process_arachne() @@ -406,27 +504,8 @@ void PerimeterGenerator::process_arachne() } } - for (const Arachne::ExtrusionLine *extrusion : ordered_extrusions) { - if (extrusion->empty()) - continue; - - ExtrusionEntityCollection entities_coll; - - ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion); - bool ext_perimeter = extrusion->inset_idx == 0; - ExtrusionPaths paths = thick_polyline_to_extrusion_paths(thick_polyline, ext_perimeter ? erExternalPerimeter : erPerimeter, - ext_perimeter ? this->ext_perimeter_flow : this->perimeter_flow, scaled(0.05), 0); - // Append paths to collection. - if (!paths.empty()) { - if (paths.front().first_point() == paths.back().last_point()) - entities_coll.entities.emplace_back(new ExtrusionLoop(std::move(paths))); - else - for (ExtrusionPath &path : paths) - entities_coll.entities.emplace_back(new ExtrusionPath(std::move(path))); - } - - this->loops->append(entities_coll); - } + if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions); !extrusion_coll.empty()) + this->loops->append(extrusion_coll); ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); // create one more offset to be used as boundary for fill