Merge branch 'lh_arachne'
This commit is contained in:
commit
0b7e21e21c
@ -761,7 +761,7 @@ bool WallToolPaths::removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpa
|
|||||||
*
|
*
|
||||||
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
|
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
|
||||||
*/
|
*/
|
||||||
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> WallToolPaths::getRegionOrder(const std::vector<const ExtrusionLine *> &input, const bool outer_to_inner)
|
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> WallToolPaths::getRegionOrder(const std::vector<ExtrusionLine *> &input, const bool outer_to_inner)
|
||||||
{
|
{
|
||||||
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> order_requirements;
|
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> order_requirements;
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ public:
|
|||||||
*
|
*
|
||||||
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
|
* \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<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> getRegionOrder(const std::vector<const ExtrusionLine *> &input, bool outer_to_inner);
|
static std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> getRegionOrder(const std::vector<ExtrusionLine *> &input, bool outer_to_inner);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/*!
|
/*!
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "ExtrusionLine.hpp"
|
#include "ExtrusionLine.hpp"
|
||||||
#include "linearAlg2D.hpp"
|
#include "linearAlg2D.hpp"
|
||||||
|
#include "../../PerimeterGenerator.hpp"
|
||||||
|
|
||||||
namespace Slic3r::Arachne
|
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<float>(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<float>(0.05), 0));
|
||||||
|
}
|
||||||
|
} // namespace Slic3r
|
@ -9,6 +9,9 @@
|
|||||||
#include "../../Polyline.hpp"
|
#include "../../Polyline.hpp"
|
||||||
#include "../../Polygon.hpp"
|
#include "../../Polygon.hpp"
|
||||||
#include "../../BoundingBox.hpp"
|
#include "../../BoundingBox.hpp"
|
||||||
|
#include "../../ExtrusionEntity.hpp"
|
||||||
|
#include "../../Flow.hpp"
|
||||||
|
#include "../../../clipper/clipper_z.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
class ThickPolyline;
|
class ThickPolyline;
|
||||||
@ -208,6 +211,26 @@ static inline Slic3r::ThickPolyline to_thick_polyline(const Arachne::ExtrusionLi
|
|||||||
return out;
|
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)
|
static inline Polygon to_polygon(const ExtrusionLine &line)
|
||||||
{
|
{
|
||||||
Polygon out;
|
Polygon out;
|
||||||
@ -269,4 +292,12 @@ static std::vector<Points> to_points(const std::vector<const ExtrusionLine *> &e
|
|||||||
using VariableWidthLines = std::vector<ExtrusionLine>; //<! The ExtrusionLines generated by libArachne
|
using VariableWidthLines = std::vector<ExtrusionLine>; //<! The ExtrusionLines generated by libArachne
|
||||||
|
|
||||||
} // namespace Slic3r::Arachne
|
} // namespace Slic3r::Arachne
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
void extrusion_paths_append(ExtrusionPaths &dst, const ClipperLib_Z::Paths &extrusion_paths, const ExtrusionRole role, const Flow &flow);
|
||||||
|
void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow);
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif // UTILS_EXTRUSION_LINE_H
|
#endif // UTILS_EXTRUSION_LINE_H
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
#include "ExtrusionEntityCollection.hpp"
|
#include "ExtrusionEntityCollection.hpp"
|
||||||
#include "ShortestPath.hpp"
|
#include "ShortestPath.hpp"
|
||||||
|
#include "clipper/clipper_z.hpp"
|
||||||
|
|
||||||
#include "Arachne/WallToolPaths.hpp"
|
#include "Arachne/WallToolPaths.hpp"
|
||||||
|
#include "Arachne/utils/ExtrusionLine.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@ -64,7 +67,7 @@ ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_poly
|
|||||||
path.polyline.append(line.b);
|
path.polyline.append(line.b);
|
||||||
// Convert from spacing to extrusion width based on the extrusion model
|
// Convert from spacing to extrusion width based on the extrusion model
|
||||||
// of a square extrusion ended with semi circles.
|
// of a square extrusion ended with semi circles.
|
||||||
Flow new_flow = flow.with_width(unscale<float>(w) + flow.height() * float(1. - 0.25 * PI));
|
Flow new_flow = (role == erOverhangPerimeter && flow.bridge()) ? flow : flow.with_width(unscale<float>(w) + flow.height() * float(1. - 0.25 * PI));
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_DEBUG
|
||||||
printf(" filling %f gap\n", flow.width);
|
printf(" filling %f gap\n", flow.width);
|
||||||
#endif
|
#endif
|
||||||
@ -169,6 +172,43 @@ static void fuzzy_polygon(Polygon &poly, double fuzzy_skin_thickness, double fuz
|
|||||||
poly.points = std::move(out);
|
poly.points = std::move(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Thanks Cura developers for this function.
|
||||||
|
static void fuzzy_extrusion_line(Arachne::ExtrusionLine &ext_lines, double fuzzy_skin_thickness, double fuzzy_skin_point_dist)
|
||||||
|
{
|
||||||
|
const double min_dist_between_points = fuzzy_skin_point_dist * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||||
|
const double range_random_point_dist = fuzzy_skin_point_dist / 2.;
|
||||||
|
double dist_left_over = double(rand()) * (min_dist_between_points / 2) / double(RAND_MAX); // the distance to be traversed on the line before making the first new point
|
||||||
|
|
||||||
|
auto * p0 = &ext_lines.junctions.back();
|
||||||
|
std::vector<Arachne::ExtrusionJunction> out;
|
||||||
|
out.reserve(ext_lines.size());
|
||||||
|
for (auto &p1 : ext_lines)
|
||||||
|
{ // 'a' is the (next) new point between p0 and p1
|
||||||
|
Vec2d p0p1 = (p1.p - p0->p).cast<double>();
|
||||||
|
double p0p1_size = p0p1.norm();
|
||||||
|
// so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
|
||||||
|
double dist_last_point = dist_left_over + p0p1_size * 2.;
|
||||||
|
for (double p0pa_dist = dist_left_over; p0pa_dist < p0p1_size;
|
||||||
|
p0pa_dist += min_dist_between_points + double(rand()) * range_random_point_dist / double(RAND_MAX))
|
||||||
|
{
|
||||||
|
double r = double(rand()) * (fuzzy_skin_thickness * 2.) / double(RAND_MAX) - fuzzy_skin_thickness;
|
||||||
|
out.emplace_back(p0->p + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast<double>().normalized() * r).cast<coord_t>(), p1.w, p1.perimeter_index);
|
||||||
|
dist_last_point = p0pa_dist;
|
||||||
|
}
|
||||||
|
dist_left_over = p0p1_size - dist_last_point;
|
||||||
|
p0 = &p1;
|
||||||
|
}
|
||||||
|
while (out.size() < 3) {
|
||||||
|
size_t point_idx = ext_lines.size() - 2;
|
||||||
|
out.emplace_back(ext_lines[point_idx].p, ext_lines[point_idx].w, ext_lines[point_idx].perimeter_index);
|
||||||
|
if (point_idx == 0)
|
||||||
|
break;
|
||||||
|
-- point_idx;
|
||||||
|
}
|
||||||
|
if (out.size() >= 3)
|
||||||
|
ext_lines.junctions = std::move(out);
|
||||||
|
}
|
||||||
|
|
||||||
using PerimeterGeneratorLoops = std::vector<PerimeterGeneratorLoop>;
|
using PerimeterGeneratorLoops = std::vector<PerimeterGeneratorLoop>;
|
||||||
|
|
||||||
static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
|
static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
|
||||||
@ -277,6 +317,112 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||||||
return out;
|
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<double>().squaredNorm();
|
||||||
|
double dist_sqr = (pt - start).cast<double>().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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PerimeterGeneratorArachneExtrusion
|
||||||
|
{
|
||||||
|
Arachne::ExtrusionLine *extrusion = nullptr;
|
||||||
|
// Should this extrusion be fuzzyfied on path generation?
|
||||||
|
bool fuzzify = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &perimeter_generator, std::vector<PerimeterGeneratorArachneExtrusion> &pg_extrusions)
|
||||||
|
{
|
||||||
|
ExtrusionEntityCollection extrusion_coll;
|
||||||
|
for (PerimeterGeneratorArachneExtrusion &pg_extrusion : pg_extrusions) {
|
||||||
|
Arachne::ExtrusionLine *extrusion = pg_extrusion.extrusion;
|
||||||
|
if (extrusion->empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const bool is_external = extrusion->inset_idx == 0;
|
||||||
|
ExtrusionRole role = is_external ? erExternalPerimeter : erPerimeter;
|
||||||
|
|
||||||
|
if (pg_extrusion.fuzzify)
|
||||||
|
fuzzy_extrusion_line(*extrusion, scaled<float>(perimeter_generator.config->fuzzy_skin_thickness.value), scaled<float>(perimeter_generator.config->fuzzy_skin_point_dist.value));
|
||||||
|
|
||||||
|
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
|
// 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"
|
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
|
||||||
void PerimeterGenerator::process_arachne()
|
void PerimeterGenerator::process_arachne()
|
||||||
@ -327,11 +473,11 @@ void PerimeterGenerator::process_arachne()
|
|||||||
direction = 1;
|
direction = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const Arachne::ExtrusionLine *> all_extrusions;
|
std::vector<Arachne::ExtrusionLine *> all_extrusions;
|
||||||
for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) {
|
for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) {
|
||||||
if (perimeters[perimeter_idx].empty())
|
if (perimeters[perimeter_idx].empty())
|
||||||
continue;
|
continue;
|
||||||
for (const Arachne::ExtrusionLine &wall : perimeters[perimeter_idx])
|
for (Arachne::ExtrusionLine &wall : perimeters[perimeter_idx])
|
||||||
all_extrusions.emplace_back(&wall);
|
all_extrusions.emplace_back(&wall);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,9 +495,9 @@ void PerimeterGenerator::process_arachne()
|
|||||||
blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second);
|
blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<bool> processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed.
|
std::vector<bool> processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed.
|
||||||
Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position.
|
Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position.
|
||||||
std::vector<const Arachne::ExtrusionLine *> ordered_extrusions; // To store our result in. At the end we'll std::swap.
|
std::vector<PerimeterGeneratorArachneExtrusion> ordered_extrusions; // To store our result in. At the end we'll std::swap.
|
||||||
ordered_extrusions.reserve(all_extrusions.size());
|
ordered_extrusions.reserve(all_extrusions.size());
|
||||||
|
|
||||||
while (ordered_extrusions.size() < all_extrusions.size()) {
|
while (ordered_extrusions.size() < all_extrusions.size()) {
|
||||||
@ -393,7 +539,7 @@ void PerimeterGenerator::process_arachne()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto &best_path = all_extrusions[best_candidate];
|
auto &best_path = all_extrusions[best_candidate];
|
||||||
ordered_extrusions.push_back(best_path);
|
ordered_extrusions.push_back({best_path, false});
|
||||||
processed[best_candidate] = true;
|
processed[best_candidate] = true;
|
||||||
for (size_t unlocked_idx : blocking[best_candidate])
|
for (size_t unlocked_idx : blocking[best_candidate])
|
||||||
blocked[unlocked_idx]--;
|
blocked[unlocked_idx]--;
|
||||||
@ -406,28 +552,49 @@ void PerimeterGenerator::process_arachne()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const Arachne::ExtrusionLine *extrusion : ordered_extrusions) {
|
if (this->layer_id > 0 && this->config->fuzzy_skin != FuzzySkinType::None) {
|
||||||
if (extrusion->empty())
|
std::vector<PerimeterGeneratorArachneExtrusion *> closed_loop_extrusions;
|
||||||
continue;
|
for (PerimeterGeneratorArachneExtrusion &extrusion : ordered_extrusions)
|
||||||
|
if (extrusion.extrusion->inset_idx == 0) {
|
||||||
|
if (extrusion.extrusion->is_closed && this->config->fuzzy_skin == FuzzySkinType::External) {
|
||||||
|
closed_loop_extrusions.emplace_back(&extrusion);
|
||||||
|
} else {
|
||||||
|
extrusion.fuzzify = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ExtrusionEntityCollection entities_coll;
|
if (this->config->fuzzy_skin == FuzzySkinType::External) {
|
||||||
|
ClipperLib_Z::Paths loops_paths;
|
||||||
|
loops_paths.reserve(closed_loop_extrusions.size());
|
||||||
|
for (const auto &cl_extrusion : closed_loop_extrusions) {
|
||||||
|
assert(cl_extrusion->extrusion->junctions.front() == cl_extrusion->extrusion->junctions.back());
|
||||||
|
size_t loop_idx = &cl_extrusion - &closed_loop_extrusions.front();
|
||||||
|
ClipperLib_Z::Path loop_path;
|
||||||
|
loop_path.reserve(cl_extrusion->extrusion->junctions.size() - 1);
|
||||||
|
for (auto junction_it = cl_extrusion->extrusion->junctions.begin(); junction_it != std::prev(cl_extrusion->extrusion->junctions.end()); ++junction_it)
|
||||||
|
loop_path.emplace_back(junction_it->p.x(), junction_it->p.y(), loop_idx);
|
||||||
|
loops_paths.emplace_back(loop_path);
|
||||||
|
}
|
||||||
|
|
||||||
ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion);
|
ClipperLib_Z::Clipper clipper;
|
||||||
bool ext_perimeter = extrusion->inset_idx == 0;
|
clipper.AddPaths(loops_paths, ClipperLib_Z::ptSubject, true);
|
||||||
ExtrusionPaths paths = thick_polyline_to_extrusion_paths(thick_polyline, ext_perimeter ? erExternalPerimeter : erPerimeter,
|
ClipperLib_Z::PolyTree loops_polytree;
|
||||||
ext_perimeter ? this->ext_perimeter_flow : this->perimeter_flow, scaled<float>(0.05), 0);
|
clipper.Execute(ClipperLib_Z::ctUnion, loops_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
||||||
// Append paths to collection.
|
|
||||||
if (!paths.empty()) {
|
for (const ClipperLib_Z::PolyNode *child_node : loops_polytree.Childs) {
|
||||||
if (paths.front().first_point() == paths.back().last_point())
|
// The whole contour must have the same index.
|
||||||
entities_coll.entities.emplace_back(new ExtrusionLoop(std::move(paths)));
|
coord_t polygon_idx = child_node->Contour.front().z();
|
||||||
else
|
bool has_same_idx = std::all_of(child_node->Contour.begin(), child_node->Contour.end(),
|
||||||
for (ExtrusionPath &path : paths)
|
[&polygon_idx](const ClipperLib_Z::IntPoint &point) -> bool { return polygon_idx == point.z(); });
|
||||||
entities_coll.entities.emplace_back(new ExtrusionPath(std::move(path)));
|
if (has_same_idx)
|
||||||
|
closed_loop_extrusions[polygon_idx]->fuzzify = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||||
// create one more offset to be used as boundary for fill
|
// create one more offset to be used as boundary for fill
|
||||||
// we offset by half the perimeter spacing (to get to the actual infill boundary)
|
// we offset by half the perimeter spacing (to get to the actual infill boundary)
|
||||||
|
@ -72,7 +72,7 @@ private:
|
|||||||
Polygons m_lower_slices_polygons;
|
Polygons m_lower_slices_polygons;
|
||||||
};
|
};
|
||||||
|
|
||||||
ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance, const float merge_tolerance);
|
ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,8 +257,6 @@ use Slic3r::Test;
|
|||||||
$config->set('bridge_fan_speed', [ 100 ]);
|
$config->set('bridge_fan_speed', [ 100 ]);
|
||||||
$config->set('bridge_flow_ratio', 33); # arbitrary value
|
$config->set('bridge_flow_ratio', 33); # arbitrary value
|
||||||
$config->set('overhangs', 1);
|
$config->set('overhangs', 1);
|
||||||
# FIXME Lukas H.: For now, this unit test is disabled for Arachne because of an issue with detecting overhang when Arachne is enabled.
|
|
||||||
$config->set('perimeter_generator', 'classic');
|
|
||||||
my $print = Slic3r::Test::init_print('overhang', config => $config);
|
my $print = Slic3r::Test::init_print('overhang', config => $config);
|
||||||
my %layer_speeds = (); # print Z => [ speeds ]
|
my %layer_speeds = (); # print Z => [ speeds ]
|
||||||
my $fan_speed = 0;
|
my $fan_speed = 0;
|
||||||
@ -397,8 +395,6 @@ use Slic3r::Test;
|
|||||||
$config->set('overhangs', 1);
|
$config->set('overhangs', 1);
|
||||||
$config->set('cooling', [ 0 ]); # to prevent speeds from being altered
|
$config->set('cooling', [ 0 ]); # to prevent speeds from being altered
|
||||||
$config->set('first_layer_speed', '100%'); # to prevent speeds from being altered
|
$config->set('first_layer_speed', '100%'); # to prevent speeds from being altered
|
||||||
# FIXME Lukas H.: For now, this unit test is disabled for Arachne because of an issue with detecting overhang when Arachne is enabled.
|
|
||||||
$config->set('perimeter_generator', 'classic');
|
|
||||||
|
|
||||||
my $test = sub {
|
my $test = sub {
|
||||||
my ($print) = @_;
|
my ($print) = @_;
|
||||||
|
@ -20,10 +20,7 @@ SCENARIO("PrintObject: Perimeter generation", "[PrintObject]") {
|
|||||||
}
|
}
|
||||||
THEN("Every layer in region 0 has 1 island of perimeters") {
|
THEN("Every layer in region 0 has 1 island of perimeters") {
|
||||||
for (const Layer *layer : object.layers())
|
for (const Layer *layer : object.layers())
|
||||||
if (object.config().perimeter_generator == PerimeterGeneratorType::Arachne)
|
REQUIRE(layer->regions().front()->perimeters.entities.size() == 1);
|
||||||
REQUIRE(layer->regions().front()->perimeters.entities.size() == 3);
|
|
||||||
else
|
|
||||||
REQUIRE(layer->regions().front()->perimeters.entities.size() == 1);
|
|
||||||
}
|
}
|
||||||
THEN("Every layer in region 0 has 3 paths in its perimeters list.") {
|
THEN("Every layer in region 0 has 3 paths in its perimeters list.") {
|
||||||
for (const Layer *layer : object.layers())
|
for (const Layer *layer : object.layers())
|
||||||
|
Loading…
Reference in New Issue
Block a user