ENH: new overhang alogrithm for arachne

1. Speed up the wall generate

jira:STUDIO-11630

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: Ibb4129b9530095296e5e74412a44aeb62d3497e5
This commit is contained in:
xun.zhang 2025-04-25 11:32:25 +08:00 committed by lane.wei
parent 07516d1fec
commit e13c98e4ad
5 changed files with 399 additions and 199 deletions

View File

@ -244,6 +244,8 @@ set(lisbslic3r_sources
ParameterUtils.hpp ParameterUtils.hpp
PerimeterGenerator.cpp PerimeterGenerator.cpp
PerimeterGenerator.hpp PerimeterGenerator.hpp
OverhangDetector.hpp
OverhangDetector.cpp
PlaceholderParser.cpp PlaceholderParser.cpp
PlaceholderParser.hpp PlaceholderParser.hpp
Platform.cpp Platform.cpp

View File

@ -0,0 +1,306 @@
#include "OverhangDetector.hpp"
#include "ExtrusionEntity.hpp"
#include "AABBTreeLines.hpp"
#include "Arachne/utils/ExtrusionLine.hpp"
namespace Slic3r {
Polylines ZPath_to_polylines(const ZPaths& paths)
{
Polylines lines;
for (auto& path : paths) {
lines.emplace_back();
for (auto& p : path) lines.back().points.push_back(Point{ p.x(), p.y() });
}
return lines;
};
ZPaths clip_extrusion(const ZPath& subject, const ZPaths& 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) {
// The clipping contour may be simplified by clipping it with a bounding box of "subject" path.
// The clipping function used may produce self intersections outside of the "subject" bounding box. Such self intersections are
// harmless to the result of the clipping operation,
// Both ends of each edge belong to the same source: Either they are from subject or from clipping path.
assert(e1bot.z() >= 0 && e1top.z() >= 0);
assert(e2bot.z() >= 0 && e2top.z() >= 0);
assert((e1bot.z() == 0) == (e1top.z() == 0));
assert((e2bot.z() == 0) == (e2top.z() == 0));
// Start & end points of the clipped polyline (extrusion path with a non-zero width).
ClipperLib_Z::IntPoint start = e1bot;
ClipperLib_Z::IntPoint end = e1top;
if (start.z() <= 0 && end.z() <= 0) {
start = e2bot;
end = e2top;
}
if (start.z() <= 0 && end.z() <= 0) {
// Self intersection on the source contour.
assert(start.z() == 0 && end.z() == 0);
pt.z() = 0;
}
else {
// Interpolate extrusion line width.
assert(start.z() > 0 && end.z() > 0);
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);
// Clipped path could contain vertices from the clip with a Z coordinate equal to zero.
// For those vertices, we must assign value based on the subject.
// This happens only in sporadic cases.
for (ClipperLib_Z::Path& path : clipped_paths)
for (ClipperLib_Z::IntPoint& c_pt : path)
if (c_pt.z() == 0) {
// Now we must find the corresponding line on with this point is located and compute line width (Z coordinate).
if (subject.size() <= 2) continue;
const Point pt(c_pt.x(), c_pt.y());
Point projected_pt_min;
auto it_min = subject.begin();
auto dist_sqr_min = std::numeric_limits<double>::max();
Point prev(subject.front().x(), subject.front().y());
for (auto it = std::next(subject.begin()); it != subject.end(); ++it) {
Point curr(it->x(), it->y());
Point projected_pt = pt.projection_onto(Line(prev, curr));
if (double dist_sqr = (projected_pt - pt).cast<double>().squaredNorm(); dist_sqr < dist_sqr_min) {
dist_sqr_min = dist_sqr;
projected_pt_min = projected_pt;
it_min = std::prev(it);
}
prev = curr;
}
assert(dist_sqr_min <= SCALED_EPSILON);
assert(std::next(it_min) != subject.end());
const Point pt_a(it_min->x(), it_min->y());
const Point pt_b(std::next(it_min)->x(), std::next(it_min)->y());
const double line_len = (pt_b - pt_a).cast<double>().norm();
const double dist = (projected_pt_min - pt_a).cast<double>().norm();
c_pt.z() = coord_t(double(it_min->z()) + (dist / line_len) * double(std::next(it_min)->z() - it_min->z()));
}
assert([&clipped_paths = std::as_const(clipped_paths)]() -> bool {
for (const ClipperLib_Z::Path& path : clipped_paths)
for (const ClipperLib_Z::IntPoint& pt : path)
if (pt.z() <= 0) return false;
return true;
}());
return clipped_paths;
}
ZPath add_sampling_points(const ZPath& path, double min_sampling_interval)
{
ZPath sampled_path;
if (path.empty())
return sampled_path;
for (size_t idx = 0; idx < path.size(); ++idx) {
ZPoint curr_zp = path[idx];
Point curr_p = { curr_zp.x(), curr_zp.y() };
sampled_path.emplace_back(curr_zp);
if (idx + 1 < path.size()) {
ZPoint next_zp = path[idx + 1];
Point next_p = { next_zp.x(), next_zp.y() };
double dist = (next_p - curr_p).cast<double>().norm();
if (dist > min_sampling_interval) {
size_t num_samples = static_cast<size_t>(std::floor(dist / min_sampling_interval));
for (size_t j = 1; j <= num_samples; ++j) {
double t = j * min_sampling_interval / dist;
ZPoint new_point;
new_point.x() = curr_p.x() + t * (next_p.x() - curr_p.x());
new_point.y() = curr_p.y() + t * (next_p.y() - curr_p.y());
new_point.z() = curr_zp.z() + t * (next_zp.z() - curr_zp.z());
sampled_path.push_back(new_point);
}
}
}
}
return sampled_path;
}
OverhangDistancer::OverhangDistancer(const Polygons layer_polygons)
{
for (const Polygon& island : layer_polygons) {
for (const auto& line : island.lines()) { lines.emplace_back(line.a.cast<double>(), line.b.cast<double>()); }
}
tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines);
}
float OverhangDistancer::distance_from_perimeter(const Vec2f& point) const
{
Vec2d p = point.cast<double>();
size_t hit_idx_out{};
Vec2d hit_point_out = Vec2d::Zero();
auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out);
if (distance < 0) { return std::numeric_limits<float>::max(); }
distance = sqrt(distance);
return distance;
}
ExtrusionPaths detect_overhang_degree(const Flow& flow,
const ExtrusionRole role,
const Polygons& lower_polys,
const ClipperLib_Z::Paths& clip_paths,
const ClipperLib_Z::Path& extrusion_path,
const double nozzle_diameter)
{
using ZPath = ClipperLib_Z::Path;
using ZPaths = ClipperLib_Z::Paths;
using ZPoint = ClipperLib_Z::IntPoint;
ExtrusionPaths ret_paths;
ZPath subject_path = add_sampling_points(extrusion_path, scale_(2));
//Polylines polylines = ZPath_to_polylines(subject_paths);
ZPaths paths_in_range = clip_extrusion(subject_path, clip_paths, ClipperLib_Z::ctIntersection);
//Polylines path_in_range_debug = ZPath_to_polylines(paths_in_range);
for (auto& path : paths_in_range) {
for (auto& p : path) { assert(p.z() != 0); }
}
auto in_same_degree_range = [](double a, double b) -> bool {
int ceil_a = std::ceil(a);
int floor_a = std::floor(a);
int ceil_b = std::ceil(b);
int floor_b = std::floor(b);
return ceil_a == ceil_b && floor_a == floor_b;
};
struct SplitPoint
{
Point p;
coord_t w;
double degree;
};
auto get_split_points = [](const Point& pa, const Point& pb, const coord_t wa, const coord_t wb, const double da, const double db) -> std::vector<SplitPoint> {
std::vector<SplitPoint> ret;
int start_d = (int)(std::ceil(std::min(da, db)));
int end_d = (int)(std::floor(std::max(da, db)));
if (start_d > end_d) return ret;
double delta_d = db - da;
if (std::abs(delta_d) < 1e-6) return ret;
if (da < db) {
for (int k = start_d; k <= end_d; ++k) {
const double t = (k - da) / delta_d;
const Point pt = pa + (pb - pa) * t;
const coord_t w = wa + coord_t((wb - wa) * t);
ret.emplace_back(SplitPoint{ pt, w, (double)k });
}
}
else {
for (int k = end_d; k >= start_d; --k) {
const double t = (k - da) / delta_d;
const Point pt = pa + (pb - pa) * t;
const coord_t w = wa + coord_t((wb - wa) * t);
ret.emplace_back(SplitPoint{ pt, w, (double)k });
}
}
return ret;
};
std::unique_ptr<OverhangDistancer> prev_layer_distancer = std::make_unique<OverhangDistancer>(lower_polys);
coord_t offset_width = scale_(nozzle_diameter) / 2;
for (auto& path : paths_in_range) {
std::vector<double> overhang_degree_arr;
if (path.empty()) continue;
for (size_t idx = 0; idx < path.size(); ++idx) {
Point p{ path[idx].x(), path[idx].y() };
float overhang_dist = prev_layer_distancer->distance_from_perimeter(p.cast<float>());
float width = path[idx].z();
double real_dist = offset_width - overhang_dist;
double degree = 0;
if (std::abs(real_dist) > (width / 2)) {
degree = real_dist < 0 ? 0 : 100;
}
else {
degree = (width / 2 + real_dist) / width * 100;
}
double mapped_degree;
// map overhang speed to a range
{
auto it = std::upper_bound(non_uniform_degree_map.begin(), non_uniform_degree_map.end(), degree);
int high_idx = it - non_uniform_degree_map.begin();
int low_idx = high_idx - 1;
double t = (degree - non_uniform_degree_map[low_idx]) / (non_uniform_degree_map[high_idx] - non_uniform_degree_map[low_idx]);
mapped_degree = low_idx * (1 - t) + t * high_idx;
}
overhang_degree_arr.emplace_back(mapped_degree);
}
// split into extrusion path
Point prev_p{ path.front().x(), path.front().y() };
coord_t prev_w = path.front().z();
double prev_d = overhang_degree_arr.front();
ZPath prev_line = { path.front() };
for (size_t idx = 1; idx < path.size(); ++idx) {
Point curr_p{ path[idx].x(), path[idx].y() };
coord_t curr_w = path[idx].z();
double curr_d = overhang_degree_arr[idx];
if (in_same_degree_range(prev_d, curr_d)) {
prev_w = curr_w;
prev_d = curr_d;
prev_p = curr_p;
prev_line.push_back(path[idx]);
continue;
}
else {
auto split_points = get_split_points(prev_p, curr_p, prev_w, curr_w, prev_d, curr_d);
for (auto& split_point : split_points) {
prev_line.push_back({ split_point.p.x(), split_point.p.y(), split_point.w });
double target_degree = 0;
if (prev_d < curr_d)
target_degree = split_point.degree - 1;
else
target_degree = split_point.degree;
extrusion_paths_append(ret_paths, { prev_line }, role, flow, target_degree);
prev_line.clear();
prev_line.push_back({ split_point.p.x(), split_point.p.y(), split_point.w });
}
prev_w = curr_w;
prev_d = curr_d;
prev_p = curr_p;
prev_line.push_back(path[idx]);
}
}
if (prev_line.size() > 1) { extrusion_paths_append(ret_paths, { prev_line }, role, flow, std::floor(prev_d)); }
}
return ret_paths;
}
}

View File

@ -0,0 +1,40 @@
#ifndef OVERHANG_DETECTOR_HPP
#define OVERHANG_DETECTOR_HPP
#include "AABBTreeIndirect.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "ClipperUtils.hpp"
#include "Flow.hpp"
using ZPoint = ClipperLib_Z::IntPoint;
using ZPath = ClipperLib_Z::Path;
using ZPaths = ClipperLib_Z::Paths;
static const int overhang_sampling_number = 6;
static const double min_degree_gap = 0.1;
static const int max_overhang_degree = overhang_sampling_number - 1;
static const std::vector<double> non_uniform_degree_map = { 0, 10, 25, 50, 75, 100};
static const int insert_point_count = 3;
namespace Slic3r {
class OverhangDistancer
{
std::vector<Linef> lines;
AABBTreeIndirect::Tree<2, double> tree;
public:
OverhangDistancer(const Polygons layer_polygons);
float distance_from_perimeter(const Vec2f& point) const;
};
ZPaths clip_extrusion(const ZPath& subject, const ZPaths& clip, ClipperLib_Z::ClipType clipType);
ZPath add_sampling_points(const ZPath& path, double min_sampling_interval);
ExtrusionPaths detect_overhang_degree(const Flow& flow, const ExtrusionRole role, const Polygons& lower_polys, const ClipperLib_Z::Paths& clip_paths, const ClipperLib_Z::Path& extrusion_paths, const double nozzle_diameter);
}
#endif

View File

@ -13,13 +13,9 @@
#include <random> #include <random>
#include <thread> #include <thread>
#include <unordered_set> #include <unordered_set>
#include "libslic3r/AABBTreeLines.hpp" #include "OverhangDetector.hpp"
static const int overhang_sampling_number = 6;
static const double narrow_loop_length_threshold = 10; static const double narrow_loop_length_threshold = 10;
static const double min_degree_gap = 0.1;
static const int max_overhang_degree = overhang_sampling_number - 1;
static const std::vector<double> non_uniform_degree_map = { 0, 10, 25, 50, 75, 100};
static const int insert_point_count = 3;
//BBS: when the width of expolygon is smaller than //BBS: when the width of expolygon is smaller than
//ext_perimeter_width + ext_perimeter_spacing * (1 - SMALLER_EXT_INSET_OVERLAP_TOLERANCE), //ext_perimeter_width + ext_perimeter_spacing * (1 - SMALLER_EXT_INSET_OVERLAP_TOLERANCE),
//we think it's small detail area and will generate smaller line width for it //we think it's small detail area and will generate smaller line width for it
@ -311,35 +307,6 @@ static void insert_point_to_line( double left_point_degree,
} }
} }
class OverhangDistancer
{
std::vector<Linef> lines;
AABBTreeIndirect::Tree<2, double> tree;
public:
OverhangDistancer(const Polygons layer_polygons)
{
for (const Polygon &island : layer_polygons) {
for (const auto &line : island.lines()) {
lines.emplace_back(line.a.cast<double>(), line.b.cast<double>());
}
}
tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines);
}
float distance_from_perimeter(const Vec2f &point) const
{
Vec2d p = point.cast<double>();
size_t hit_idx_out{};
Vec2d hit_point_out = Vec2d::Zero();
auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out);
if (distance < 0) { return std::numeric_limits<float>::max(); }
distance = sqrt(distance);
return distance;
}
};
static void sampling_at_line_end(Polyline &poly, double mini_length, int insert_count) static void sampling_at_line_end(Polyline &poly, double mini_length, int insert_count)
{ {
@ -371,6 +338,7 @@ static void sampling_at_line_end(Polyline &poly, double mini_length, int insert_
poly.append(end_point); poly.append(end_point);
} }
static std::deque<PolylineWithDegree> detect_overahng_degree(Polygons lower_polygons, static std::deque<PolylineWithDegree> detect_overahng_degree(Polygons lower_polygons,
Polylines middle_overhang_polyines, Polylines middle_overhang_polyines,
const double &lower_bound, const double &lower_bound,
@ -728,99 +696,6 @@ 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) {
// The clipping contour may be simplified by clipping it with a bounding box of "subject" path.
// The clipping function used may produce self intersections outside of the "subject" bounding box. Such self intersections are
// harmless to the result of the clipping operation,
// Both ends of each edge belong to the same source: Either they are from subject or from clipping path.
assert(e1bot.z() >= 0 && e1top.z() >= 0);
assert(e2bot.z() >= 0 && e2top.z() >= 0);
assert((e1bot.z() == 0) == (e1top.z() == 0));
assert((e2bot.z() == 0) == (e2top.z() == 0));
// Start & end points of the clipped polyline (extrusion path with a non-zero width).
ClipperLib_Z::IntPoint start = e1bot;
ClipperLib_Z::IntPoint end = e1top;
if (start.z() <= 0 && end.z() <= 0) {
start = e2bot;
end = e2top;
}
if (start.z() <= 0 && end.z() <= 0) {
// Self intersection on the source contour.
assert(start.z() == 0 && end.z() == 0);
pt.z() = 0;
}
else {
// Interpolate extrusion line width.
assert(start.z() > 0 && end.z() > 0);
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);
// Clipped path could contain vertices from the clip with a Z coordinate equal to zero.
// For those vertices, we must assign value based on the subject.
// This happens only in sporadic cases.
for (ClipperLib_Z::Path& path : clipped_paths)
for (ClipperLib_Z::IntPoint& c_pt : path)
if (c_pt.z() == 0) {
// Now we must find the corresponding line on with this point is located and compute line width (Z coordinate).
if (subject.size() <= 2)
continue;
const Point pt(c_pt.x(), c_pt.y());
Point projected_pt_min;
auto it_min = subject.begin();
auto dist_sqr_min = std::numeric_limits<double>::max();
Point prev(subject.front().x(), subject.front().y());
for (auto it = std::next(subject.begin()); it != subject.end(); ++it) {
Point curr(it->x(), it->y());
Point projected_pt = pt.projection_onto(Line(prev, curr));
if (double dist_sqr = (projected_pt - pt).cast<double>().squaredNorm(); dist_sqr < dist_sqr_min) {
dist_sqr_min = dist_sqr;
projected_pt_min = projected_pt;
it_min = std::prev(it);
}
prev = curr;
}
assert(dist_sqr_min <= SCALED_EPSILON);
assert(std::next(it_min) != subject.end());
const Point pt_a(it_min->x(), it_min->y());
const Point pt_b(std::next(it_min)->x(), std::next(it_min)->y());
const double line_len = (pt_b - pt_a).cast<double>().norm();
const double dist = (projected_pt_min - pt_a).cast<double>().norm();
c_pt.z() = coord_t(double(it_min->z()) + (dist / line_len) * double(std::next(it_min)->z() - it_min->z()));
}
assert([&clipped_paths = std::as_const(clipped_paths)]() -> bool {
for (const ClipperLib_Z::Path& path : clipped_paths)
for (const ClipperLib_Z::IntPoint& pt : path)
if (pt.z() <= 0)
return false;
return true;
}());
return clipped_paths;
}
struct PerimeterGeneratorArachneExtrusion struct PerimeterGeneratorArachneExtrusion
{ {
@ -941,6 +816,9 @@ static void detect_brigde_wall_arachne(const PerimeterGenerator &perimeter_gener
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& perimeter_generator, std::vector<PerimeterGeneratorArachneExtrusion>& pg_extrusions) static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& perimeter_generator, std::vector<PerimeterGeneratorArachneExtrusion>& pg_extrusions)
{ {
using ZPath = ClipperLib_Z::Path;
using ZPaths = ClipperLib_Z::Paths;
ExtrusionEntityCollection extrusion_coll; ExtrusionEntityCollection extrusion_coll;
if (perimeter_generator.print_config->z_direction_outwall_speed_continuous) if (perimeter_generator.print_config->z_direction_outwall_speed_continuous)
extrusion_coll.loop_node_range.first = perimeter_generator.loop_nodes->size(); extrusion_coll.loop_node_range.first = perimeter_generator.loop_nodes->size();
@ -977,92 +855,59 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) { if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) {
ClipperLib_Z::Path extrusion_path; ClipperLib_Z::Path extrusion_path;
extrusion_path.reserve(extrusion->size()); extrusion_path.reserve(extrusion->size());
double nozzle_diameter = perimeter_generator.print_config->nozzle_diameter.get_at(perimeter_generator.config->wall_filament - 1);
Polygons lower_layer_polys = perimeter_generator.lower_slices_polygons();
coord_t max_extrusion_width = 0;
BoundingBox extrusion_path_bbox; BoundingBox extrusion_path_bbox;
for (const Arachne::ExtrusionJunction &ej : extrusion->junctions) { for (const Arachne::ExtrusionJunction &ej : extrusion->junctions) {
extrusion_path.emplace_back(ej.p.x(), ej.p.y(), ej.w); extrusion_path.emplace_back(ej.p.x(), ej.p.y(), ej.w);
extrusion_path_bbox.merge(Point(ej.p.x(), ej.p.y())); extrusion_path_bbox.merge(Point(ej.p.x(), ej.p.y()));
max_extrusion_width = std::max(max_extrusion_width, ej.w);
}
extrusion_path_bbox.inflated(max_extrusion_width+scale_(nozzle_diameter));
Polygons new_lower_polys;
for (size_t idx = 0; idx < lower_layer_polys.size(); ++idx) {
auto new_poly = ClipperUtils::clip_clipper_polygon_with_subject_bbox(lower_layer_polys[idx], extrusion_path_bbox,true);
if (!new_poly.empty())
new_lower_polys.emplace_back(new_poly);
} }
ClipperLib_Z::Paths lower_slices_paths; lower_layer_polys = new_lower_polys;
{
lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size());
Points clipped;
extrusion_path_bbox.offset(SCALED_EPSILON);
for (const Polygon &poly : perimeter_generator.lower_slices_polygons()) {
clipped.clear();
ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly.points, extrusion_path_bbox, clipped);
if (!clipped.empty()) {
lower_slices_paths.emplace_back();
ClipperLib_Z::Path &out = lower_slices_paths.back();
out.reserve(clipped.size());
for (const Point &pt : clipped)
out.emplace_back(pt.x(), pt.y(), 0);
}
}
}
ExtrusionPaths temp_paths; ZPath subject_path;
// get non-overhang paths by intersecting this loop with the grown lower slices for (auto& ej : extrusion->junctions)
extrusion_paths_append(temp_paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role, subject_path.emplace_back(ej.p.x(), ej.p.y(), ej.w);
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
ZPaths clip_paths;
for (auto& poly : lower_layer_polys) {
clip_paths.emplace_back();
for (auto& p : poly)
clip_paths.back().emplace_back(p.x(), p.y(), 0);
}
if (perimeter_generator.config->enable_overhang_speed.get_at(get_extruder_index(*(perimeter_generator.print_config), perimeter_generator.config->wall_filament - 1)) if (perimeter_generator.config->enable_overhang_speed.get_at(get_extruder_index(*(perimeter_generator.print_config), perimeter_generator.config->wall_filament - 1))
&& perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) { && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
bool is_external = extrusion->inset_idx == 0;
Flow flow = is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow; Flow flow = is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow;
std::map<double, std::vector<Polygons>> clipper_serise; ExtrusionRole role = is_external ? ExtrusionRole::erExternalPerimeter : ExtrusionRole::erPerimeter;
paths = detect_overhang_degree(flow, role, lower_layer_polys, clip_paths, subject_path, nozzle_diameter);
std::map<double,ExtrusionPaths> recognization_paths;
for (const ExtrusionPath &path : temp_paths) {
if (recognization_paths.count(path.width))
recognization_paths[path.width].emplace_back(std::move(path));
else
recognization_paths.insert(std::pair<double, ExtrusionPaths>(path.width, {std::move(path)}));
} }
else {
for (const auto &it : recognization_paths) { ExtrusionPaths temp_paths;
Polylines be_clipped; ZPaths path_non_overhang = clip_extrusion(subject_path, clip_paths, ClipperLib_Z::ctIntersection);
// get non-overhang paths by intersecting this loop with the grown lower slices
for (const ExtrusionPath &p : it.second) { extrusion_paths_append(temp_paths, path_non_overhang, role,
be_clipped.emplace_back(std::move(p.polyline)); is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
}
BoundingBox extrusion_bboxs = get_extents(be_clipped);
//ExPolygons lower_slcier_chopped = *perimeter_generator.lower_slices;
Polygons lower_slcier_chopped=ClipperUtils::clip_clipper_polygons_with_subject_bbox(*perimeter_generator.lower_slices, extrusion_bboxs, true);
double start_pos = -it.first * 0.5;
double end_pos = 0.5 * it.first;
Polylines remain_polylines;
std::vector<Polygons> degree_polygons;
for (int j = 0; j < overhang_sampling_number; j++) {
Polygons limiton_polygons = offset(lower_slcier_chopped, float(scale_(start_pos + (j + 0.5) * (end_pos - start_pos) / (overhang_sampling_number - 1))));
Polylines inside_polines = j == 0 ? intersection_pl_2(be_clipped, limiton_polygons) : intersection_pl_2(remain_polylines, limiton_polygons);
remain_polylines = j == 0 ? diff_pl_2(be_clipped, limiton_polygons) : diff_pl_2(remain_polylines, limiton_polygons);
extrusion_paths_append(paths, std::move(inside_polines), j, int(0), role, it.second.front().mm3_per_mm, it.second.front().width, it.second.front().height);
if (remain_polylines.size() == 0) break;
}
if (remain_polylines.size() != 0) {
extrusion_paths_append(paths, std::move(remain_polylines), overhang_sampling_number - 1, int(0), erOverhangPerimeter, it.second.front().mm3_per_mm, it.second.front().width, it.second.front().height);
}
}
} else {
paths = std::move(temp_paths); paths = std::move(temp_paths);
} }
// get overhang paths by checking what parts of this loop fall // get overhang paths by checking what parts of this loop fall
// outside the grown lower slices (thus where the distance between // outside the grown lower slices (thus where the distance between
// the loop centerline and original lower slices is >= half nozzle diameter // the loop centerline and original lower slices is >= half nozzle diameter
// detect if the overhang perimeter is bridge // detect if the overhang perimeter is bridge
ClipperLib_Z::Paths path_overhang = clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference); ZPaths path_overhang = clip_extrusion(subject_path, clip_paths, ClipperLib_Z::ctDifference);
bool zero_z_support = (perimeter_generator.object_config->enable_support || perimeter_generator.object_config->enforce_support_layers > 0) && perimeter_generator.object_config->support_top_z_distance.value == 0; bool zero_z_support = (perimeter_generator.object_config->enable_support || perimeter_generator.object_config->enforce_support_layers > 0) && perimeter_generator.object_config->support_top_z_distance.value == 0;
if(zero_z_support) if(zero_z_support)

View File

@ -423,6 +423,7 @@ void PrintObject::make_perimeters()
#endif #endif
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start";
#if 1
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) { [this](const tbb::blocked_range<size_t>& range) {
@ -432,6 +433,12 @@ void PrintObject::make_perimeters()
} }
} }
); );
#else
for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) {
m_print->throw_if_canceled();
m_layers[layer_idx]->make_perimeters();
}
#endif
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";