mirror of
https://git.mirrors.martin98.com/https://github.com/bambulab/BambuStudio.git
synced 2025-09-28 17:03:12 +08:00
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:
parent
07516d1fec
commit
e13c98e4ad
@ -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
|
||||||
|
306
src/libslic3r/OverhangDetector.cpp
Normal file
306
src/libslic3r/OverhangDetector.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
40
src/libslic3r/OverhangDetector.hpp
Normal file
40
src/libslic3r/OverhangDetector.hpp
Normal 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
|
@ -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();
|
||||||
@ -975,94 +853,61 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
|||||||
ExtrusionPaths paths;
|
ExtrusionPaths paths;
|
||||||
// detect overhanging/bridging perimeters
|
// detect overhanging/bridging perimeters
|
||||||
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) {
|
else {
|
||||||
if (recognization_paths.count(path.width))
|
ExtrusionPaths temp_paths;
|
||||||
recognization_paths[path.width].emplace_back(std::move(path));
|
ZPaths path_non_overhang = clip_extrusion(subject_path, clip_paths, ClipperLib_Z::ctIntersection);
|
||||||
else
|
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||||
recognization_paths.insert(std::pair<double, ExtrusionPaths>(path.width, {std::move(path)}));
|
extrusion_paths_append(temp_paths, path_non_overhang, role,
|
||||||
}
|
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
|
||||||
|
paths = std::move(temp_paths);
|
||||||
for (const auto &it : recognization_paths) {
|
|
||||||
Polylines be_clipped;
|
|
||||||
|
|
||||||
for (const ExtrusionPath &p : it.second) {
|
|
||||||
be_clipped.emplace_back(std::move(p.polyline));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// 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)
|
||||||
|
@ -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";
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user