889 lines
36 KiB
C++
889 lines
36 KiB
C++
#include "../ClipperUtils.hpp"
|
||
#include "../Clipper2Utils.hpp"
|
||
#include "../ClipperZUtils.hpp"
|
||
#include "../ExPolygon.hpp"
|
||
#include "../Surface.hpp"
|
||
#include "../VariableWidth.hpp"
|
||
#include "../format.hpp"
|
||
#include "FillFloatingConcentric.hpp"
|
||
#include <boost/log/trivial.hpp>
|
||
|
||
namespace Slic3r {
|
||
|
||
using ZPath = ClipperLib_Z::Path;
|
||
using ZPaths = ClipperLib_Z::Paths;
|
||
|
||
FloatingPolyline FloatingPolyline::rebase_at(size_t idx)
|
||
{
|
||
if (!this->is_closed())
|
||
return {};
|
||
|
||
FloatingPolyline ret = *this;
|
||
static_cast<Polyline&>(ret) = Polyline::rebase_at(idx);
|
||
size_t n = this->points.size();
|
||
ret.is_floating.resize(n);
|
||
for (size_t j = 0; j < n - 1; ++j) {
|
||
ret.is_floating[j] = this->is_floating[(idx + j) % (n-1)];
|
||
}
|
||
ret.is_floating.emplace_back(ret.is_floating.front());
|
||
return ret;
|
||
}
|
||
|
||
FloatingThickPolyline FloatingThickPolyline::rebase_at(size_t idx)
|
||
{
|
||
if (!this->is_closed())
|
||
return {};
|
||
FloatingThickPolyline ret = *this;
|
||
static_cast<ThickPolyline&>(ret) = ThickPolyline::rebase_at(idx);
|
||
size_t n = this->points.size();
|
||
ret.is_floating.resize(n);
|
||
for (size_t j = 0; j < n - 1; ++j) {
|
||
ret.is_floating[j] = this->is_floating[(idx + j) % (n - 1)];
|
||
}
|
||
ret.is_floating.emplace_back(ret.is_floating.front());
|
||
return ret;
|
||
}
|
||
|
||
FloatingThicklines FloatingThickPolyline::floating_thicklines() const
|
||
{
|
||
FloatingThicklines lines;
|
||
if (this->points.size() >= 2) {
|
||
lines.reserve(this->points.size() - 1);
|
||
for (size_t i = 0; i + 1 < this->points.size(); ++i)
|
||
lines.emplace_back(this->points[i], this->points[i + 1], this->width[2 * i], this->width[2 * i + 1], this->is_floating[i], this->is_floating[i + 1]);
|
||
}
|
||
return lines;
|
||
}
|
||
|
||
|
||
//BBS: new function to filter width to avoid too fragmented segments
|
||
static ExtrusionPaths floating_thick_polyline_to_extrusion_paths(const FloatingThickPolyline& floating_polyline, ExtrusionRole role, const Flow& flow, const float tolerance)
|
||
{
|
||
ExtrusionPaths paths;
|
||
ExtrusionPath path(role);
|
||
FloatingThicklines lines = floating_polyline.floating_thicklines();
|
||
|
||
size_t start_index = 0;
|
||
double max_width, min_width;
|
||
|
||
auto set_flow_for_path = [&flow](ExtrusionPath& path, double width) {
|
||
Flow new_flow = flow.with_width(unscale<float>(width) + flow.height() * float(1. - 0.25 * PI));
|
||
path.mm3_per_mm = new_flow.mm3_per_mm();
|
||
path.width = new_flow.width();
|
||
path.height = new_flow.height();
|
||
};
|
||
|
||
auto append_path_and_reset = [set_flow_for_path, role, &paths](double& length, double& sum, ExtrusionPath& path){
|
||
length = sum = 0;
|
||
paths.emplace_back(std::move(path));
|
||
path = ExtrusionPath(role);
|
||
};
|
||
|
||
for (int i = 0; i < (int)lines.size(); ++i) {
|
||
const FloatingThickline& line = lines[i];
|
||
|
||
if (i == 0) {
|
||
max_width = min_width = line.a_width;
|
||
}
|
||
|
||
const coordf_t line_len = line.length();
|
||
if (line_len < SCALED_EPSILON) continue;
|
||
|
||
double thickness_delta = std::max(fabs(max_width - line.b_width), fabs(min_width - line.b_width));
|
||
//BBS: has large difference in width
|
||
if (thickness_delta > tolerance) {
|
||
//BBS: 1 generate path from start_index to i(not included)
|
||
if (start_index != i) {
|
||
path = ExtrusionPath(role);
|
||
double length = 0, sum = 0;
|
||
bool is_floating = false;
|
||
for (int idx = start_index; idx < i; idx++) {
|
||
bool curr_floating = lines[idx].is_a_floating && lines[idx].is_b_floating;
|
||
if (curr_floating != is_floating && length != 0) {
|
||
path.polyline.append(lines[idx].a);
|
||
if (is_floating)
|
||
path.set_customize_flag(CustomizeFlag::cfFloatingVerticalShell);
|
||
set_flow_for_path(path, sum / length);
|
||
append_path_and_reset(length, sum, path);
|
||
}
|
||
is_floating = curr_floating;
|
||
|
||
double line_length = lines[idx].length();
|
||
length += line_length;
|
||
sum += line_length * (lines[idx].a_width + lines[idx].b_width) * 0.5;
|
||
path.polyline.append(lines[idx].a);
|
||
}
|
||
path.polyline.append(lines[i].a);
|
||
if (length > SCALED_EPSILON) {
|
||
if (lines[i].is_a_floating && lines[i].is_b_floating)
|
||
path.set_customize_flag(CustomizeFlag::cfFloatingVerticalShell);
|
||
set_flow_for_path(path, sum / length);
|
||
paths.emplace_back(std::move(path));
|
||
}
|
||
}
|
||
|
||
start_index = i;
|
||
max_width = line.a_width;
|
||
min_width = line.a_width;
|
||
|
||
//BBS: 2 handle the i-th segment
|
||
thickness_delta = fabs(line.a_width - line.b_width);
|
||
if (thickness_delta > tolerance) {
|
||
const unsigned int segments = (unsigned int)ceil(thickness_delta / tolerance);
|
||
const coordf_t seg_len = line_len / segments;
|
||
Points pp;
|
||
std::vector<coordf_t> width;
|
||
{
|
||
pp.push_back(line.a);
|
||
width.push_back(line.a_width);
|
||
for (size_t j = 1; j < segments; ++j) {
|
||
pp.push_back((line.a.cast<double>() + (line.b - line.a).cast<double>().normalized() * (j * seg_len)).cast<coord_t>());
|
||
|
||
coordf_t w = line.a_width + (j * seg_len) * (line.b_width - line.a_width) / line_len;
|
||
width.push_back(w);
|
||
width.push_back(w);
|
||
}
|
||
pp.push_back(line.b);
|
||
width.push_back(line.b_width);
|
||
|
||
assert(pp.size() == segments + 1u);
|
||
assert(width.size() == segments * 2);
|
||
}
|
||
|
||
// delete this line and insert new ones
|
||
lines.erase(lines.begin() + i);
|
||
for (size_t j = 0; j < segments; ++j) {
|
||
FloatingThickline new_line(pp[j], pp[j + 1],width[2 * j],width[2 * j+1], line.is_a_floating,line.is_b_floating);
|
||
lines.insert(lines.begin() + i + j, new_line);
|
||
}
|
||
--i;
|
||
continue;
|
||
}
|
||
}
|
||
//BBS: just update the max and min width and continue
|
||
else {
|
||
max_width = std::max(max_width, std::max(line.a_width, line.b_width));
|
||
min_width = std::min(min_width, std::min(line.a_width, line.b_width));
|
||
}
|
||
}
|
||
//BBS: handle the remaining segment
|
||
size_t final_size = lines.size();
|
||
if (start_index < final_size) {
|
||
path = ExtrusionPath(role);
|
||
double length = 0, sum = 0;
|
||
bool is_floating = false;
|
||
for (int idx = start_index; idx < final_size; idx++) {
|
||
bool curr_floating = lines[idx].is_a_floating && lines[idx].is_b_floating;
|
||
if (curr_floating!= is_floating && length != 0) {
|
||
path.polyline.append(lines[idx].a);
|
||
if(is_floating)
|
||
path.set_customize_flag(CustomizeFlag::cfFloatingVerticalShell);
|
||
set_flow_for_path(path, sum / length);
|
||
append_path_and_reset(length, sum, path);
|
||
}
|
||
is_floating = curr_floating;
|
||
double line_length = lines[idx].length();
|
||
length += line_length;
|
||
sum += line_length * (lines[idx].a_width + lines[idx].b_width) * 0.5;
|
||
path.polyline.append(lines[idx].a);
|
||
|
||
}
|
||
path.polyline.append(lines[final_size - 1].b);
|
||
if (length > SCALED_EPSILON) {
|
||
if (lines[final_size - 1].is_a_floating && lines[final_size - 1].is_b_floating)
|
||
path.set_customize_flag(CustomizeFlag::cfFloatingVerticalShell);
|
||
set_flow_for_path(path, sum / length);
|
||
paths.emplace_back(std::move(path));
|
||
}
|
||
}
|
||
|
||
return paths;
|
||
}
|
||
|
||
double interpolate_width(const ZPath& path,
|
||
const ThickPolyline& line,
|
||
const int subject_idx_range,
|
||
const int default_width,
|
||
size_t idx)
|
||
{
|
||
int prev_idx = idx;
|
||
while (prev_idx >= 0 && (path[prev_idx].z() < 0 || path[prev_idx].z() >= subject_idx_range))
|
||
--prev_idx;
|
||
|
||
int next_idx = idx;
|
||
while (next_idx < path.size() && (path[next_idx].z() < 0 || path[next_idx].z() >= subject_idx_range))
|
||
++next_idx;
|
||
|
||
double width_prev;
|
||
double width_next;
|
||
if (prev_idx < 0) {
|
||
width_prev = default_width;
|
||
}
|
||
else {
|
||
size_t prev_z_idx = path[prev_idx].z();
|
||
width_prev = line.get_width_at(prev_z_idx);
|
||
}
|
||
|
||
if (next_idx >= path.size()) {
|
||
width_next = default_width;
|
||
}
|
||
else {
|
||
size_t next_z_idx = path[next_idx].z();
|
||
width_next = line.get_width_at(next_z_idx);
|
||
}
|
||
Point prev(path[prev_idx].x(), path[prev_idx].y());
|
||
Point next(path[next_idx].x(), path[next_idx].y());
|
||
Point curr(path[idx].x(), path[idx].y());
|
||
double d_total = (next - prev).cast<double>().norm();
|
||
double d_curr = (curr - prev).cast<double>().norm();
|
||
double t = (d_total > 0) ? (d_curr / d_total) : 0.0;
|
||
return (1 - t) * width_prev + t * width_next;
|
||
}
|
||
|
||
FloatingThickPolyline merge_lines(ZPaths lines, const std::vector<bool>& mark_flags, const ThickPolyline& line, const int subject_idx_range ,const int default_width)
|
||
{
|
||
using PathFlag = std::vector<bool>;
|
||
using PathFlags = std::vector<PathFlag>;
|
||
|
||
std::vector<bool>used(lines.size(), false);
|
||
ZPaths merged_paths;
|
||
PathFlags merged_marks;
|
||
|
||
auto update_path_flag = [](PathFlag& mark_flags, const ZPath& path, bool mark) {
|
||
for (auto p : path)
|
||
mark_flags.emplace_back(mark);
|
||
};
|
||
|
||
std::unordered_map<int64_t, std::unordered_set<size_t>> start_z_map;
|
||
std::unordered_map<int64_t, std::unordered_set<size_t>> end_z_map;
|
||
|
||
for (size_t idx = 0; idx < lines.size(); ++idx) {
|
||
if (lines[idx].empty()) {
|
||
used[idx] = true;
|
||
continue;
|
||
}
|
||
start_z_map[lines[idx].front().z()].insert(idx);
|
||
end_z_map[lines[idx].back().z()].insert(idx);
|
||
}
|
||
|
||
auto remove_from_map = [&start_z_map, &end_z_map, &lines](size_t idx) {
|
||
if (lines[idx].empty())
|
||
return;
|
||
int64_t start_z = lines[idx].front().z();
|
||
int64_t end_z = lines[idx].back().z();
|
||
start_z_map[start_z].erase(idx);
|
||
if (start_z_map[start_z].empty())
|
||
start_z_map.erase(start_z);
|
||
end_z_map[end_z].erase(idx);
|
||
if (end_z_map[end_z].empty())
|
||
end_z_map.erase(end_z);
|
||
};
|
||
|
||
for (size_t idx = 0; idx < lines.size(); ++idx) {
|
||
if (used[idx])
|
||
continue;
|
||
ZPath curr_path = lines[idx];
|
||
PathFlag curr_mark;
|
||
update_path_flag(curr_mark, curr_path, mark_flags[idx]);
|
||
used[idx] = true;
|
||
remove_from_map(idx);
|
||
|
||
bool merged;
|
||
do {
|
||
merged = false;
|
||
int64_t curr_end = curr_path.back().z();
|
||
int64_t curr_start = curr_path.front().z();
|
||
|
||
// search after
|
||
{
|
||
if (auto start_iter = start_z_map.find(curr_end);start_iter != start_z_map.end()) {
|
||
size_t j = *start_iter->second.begin();
|
||
remove_from_map(j);
|
||
curr_path.insert(curr_path.end(), lines[j].begin(), lines[j].end());
|
||
update_path_flag(curr_mark, lines[j], mark_flags[j]);
|
||
used[j] = true;
|
||
merged = true;
|
||
}
|
||
else if (auto end_iter = end_z_map.find(curr_end); end_iter != end_z_map.end()) {
|
||
size_t j = *end_iter->second.begin();
|
||
remove_from_map(j);
|
||
std::reverse(lines[j].begin(), lines[j].end());
|
||
curr_path.insert(curr_path.end(), lines[j].begin(), lines[j].end());
|
||
update_path_flag(curr_mark, lines[j], mark_flags[j]);
|
||
used[j] = true;
|
||
merged = true;
|
||
}
|
||
}
|
||
|
||
if (merged)
|
||
continue;
|
||
|
||
//search before
|
||
{
|
||
if (auto end_iter = end_z_map.find(curr_start);end_iter != end_z_map.end()) {
|
||
size_t j = *end_iter->second.begin();
|
||
remove_from_map(j);
|
||
ZPath new_path = lines[j];
|
||
PathFlag new_mark;
|
||
update_path_flag(new_mark, new_path, mark_flags[j]);
|
||
|
||
new_path.insert(new_path.end(), curr_path.begin(), curr_path.end());
|
||
new_mark.insert(new_mark.end(), curr_mark.begin(), curr_mark.end());
|
||
curr_path = std::move(new_path);
|
||
curr_mark = std::move(new_mark);
|
||
used[j] = true;
|
||
merged = true;
|
||
}
|
||
else if (auto start_iter = start_z_map.find(curr_start); start_iter != start_z_map.end()) {
|
||
size_t j = *start_iter->second.begin();
|
||
remove_from_map(j);
|
||
ZPath new_path = lines[j];
|
||
std::reverse(new_path.begin(), new_path.end());
|
||
PathFlag new_mark;
|
||
update_path_flag(new_mark, new_path, mark_flags[j]);
|
||
|
||
new_path.insert(new_path.end(), curr_path.begin(), curr_path.end());
|
||
new_mark.insert(new_mark.end(), curr_mark.begin(), curr_mark.end());
|
||
curr_path = std::move(new_path);
|
||
curr_mark = std::move(new_mark);
|
||
used[j] = true;
|
||
merged = true;
|
||
}
|
||
}
|
||
|
||
} while (merged);
|
||
|
||
merged_paths.emplace_back(curr_path);
|
||
merged_marks.emplace_back(curr_mark);
|
||
}
|
||
|
||
assert(merged_marks.size() == 1);
|
||
|
||
FloatingThickPolyline res;
|
||
|
||
auto& valid_path = merged_paths.front();
|
||
auto& valid_mark = merged_marks.front();
|
||
|
||
for (size_t idx = 0; idx < valid_path.size(); ++idx) {
|
||
int zvalue = valid_path[idx].z();
|
||
res.points.emplace_back(valid_path[idx].x(), valid_path[idx].y());
|
||
res.is_floating.emplace_back(valid_mark[idx]);
|
||
if (0 <= zvalue && zvalue < subject_idx_range) {
|
||
res.width.emplace_back(line.get_width_at(prev_idx_modulo(zvalue, line.points)));
|
||
res.width.emplace_back(line.get_width_at(zvalue));
|
||
}
|
||
else {
|
||
double width = interpolate_width(valid_path, line, subject_idx_range, default_width, idx);
|
||
res.width.emplace_back(width);
|
||
res.width.emplace_back(width);
|
||
}
|
||
}
|
||
res.width = std::vector<coordf_t>(res.width.begin() + 1, res.width.end()-1);
|
||
assert(res.width.size() == 2 * res.points.size() - 2);
|
||
|
||
return res;
|
||
}
|
||
|
||
FloatingThickPolyline detect_floating_line(const ThickPolyline& line, const ExPolygons& floating_areas, const double default_width ,bool force_no_detect)
|
||
{
|
||
{
|
||
Polyline polyline = line;
|
||
auto bbox_line = get_extents(polyline);
|
||
auto bbox_area = get_extents(floating_areas);
|
||
if (force_no_detect || !bbox_area.overlap(bbox_line) || intersection_pl(polyline, floating_areas).empty()) {
|
||
FloatingThickPolyline res;
|
||
res.width = line.width;
|
||
res.points = line.points;
|
||
res.is_floating.resize(res.points.size(), false);
|
||
return res;
|
||
}
|
||
}
|
||
|
||
using ZPoint = ClipperLib_Z::IntPoint;
|
||
auto hash_function = [](const int a1, const int b1, const int a2, const int b2)->int32_t {
|
||
int32_t hash_val = 1000 * (a1 * 13 + b1) + (a2 * 17 + b2) + 1;
|
||
hash_val &= 0x7fffffff;
|
||
return hash_val;
|
||
};
|
||
|
||
int idx = 0;
|
||
int subject_idx_range;
|
||
ZPaths subject_paths;
|
||
{
|
||
subject_paths.emplace_back();
|
||
for (auto p : line)
|
||
subject_paths.back().emplace_back(p.x(), p.y(), idx++);
|
||
}
|
||
|
||
subject_idx_range = idx;
|
||
ZPaths clip_paths;
|
||
{
|
||
Polygons floating_polygons = to_polygons(floating_areas);
|
||
for (auto& poly : floating_polygons) {
|
||
clip_paths.emplace_back();
|
||
for (const auto& p : poly)
|
||
clip_paths.back().emplace_back(p.x(), p.y(), idx++);
|
||
}
|
||
}
|
||
|
||
ClipperLib_Z::ZFillCallback z_filler = [hash_function, subject_idx_range](const ZPoint& e1_a, const ZPoint& e1_b, const ZPoint& e2_a, const ZPoint& e2_b, ZPoint& d) {
|
||
if (e1_a.z() < subject_idx_range && e1_b.z() < subject_idx_range && e2_a.z() < subject_idx_range && e2_b.z() < subject_idx_range) {
|
||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: both point in subject : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
|
||
}
|
||
if (e1_a.z() >= subject_idx_range && e1_b.z() >= subject_idx_range && e2_a.z() >= subject_idx_range && e2_b.z() >= subject_idx_range) {
|
||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: both point in clip : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
|
||
}
|
||
if (e1_a.z() < 0 || e1_b.z() < 0 || e2_a.z() < 0 || e2_b.z() < 0)
|
||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: Encounter negative z : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
|
||
if (e1_a.z() == e1_b.z() || e2_a.z() == e2_b.z()) {
|
||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: Encounter same z in one line : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
|
||
}
|
||
if (e1_a.z() == e1_b.z() && e1_b.z() == e2_a.z() && e2_a.z() == e2_b.z()) {
|
||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: Encounter same z in both line : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
|
||
// the intersect is generated by two lines in subject
|
||
d.z() = e1_a.z();
|
||
return;
|
||
} // the intersect is generate by two line from subject and clip
|
||
d.z() = -hash_function(e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
|
||
if (d.z() >= 0)
|
||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: hash function generate postive value : %d", d.z());
|
||
};
|
||
|
||
|
||
ZPaths intersect_out;
|
||
{
|
||
ClipperLib_Z::Clipper c;
|
||
ClipperLib_Z::PolyTree polytree;
|
||
c.ZFillFunction(z_filler);
|
||
c.AddPaths(subject_paths, ClipperLib_Z::ptSubject, false);
|
||
c.AddPaths(clip_paths, ClipperLib_Z::ptClip, true);
|
||
c.Execute(ClipperLib_Z::ctIntersection, polytree, ClipperLib_Z::pftNonZero);
|
||
ClipperLib_Z::PolyTreeToPaths(std::move(polytree), intersect_out);
|
||
}
|
||
|
||
ZPaths diff_out;
|
||
{
|
||
ClipperLib_Z::Clipper c;
|
||
ClipperLib_Z::PolyTree polytree;
|
||
c.ZFillFunction(z_filler);
|
||
c.AddPaths(subject_paths, ClipperLib_Z::ptSubject, false);
|
||
c.AddPaths(clip_paths, ClipperLib_Z::ptClip, true);
|
||
c.Execute(ClipperLib_Z::ctDifference, polytree, ClipperLib_Z::pftNonZero);
|
||
ClipperLib_Z::PolyTreeToPaths(std::move(polytree), diff_out);
|
||
}
|
||
|
||
|
||
ZPaths to_merge = diff_out;
|
||
to_merge.insert(to_merge.end(), intersect_out.begin(), intersect_out.end());
|
||
std::vector<bool>floating_flags(to_merge.size(), false);
|
||
for (size_t idx = diff_out.size(); idx < diff_out.size() + intersect_out.size(); ++idx)
|
||
floating_flags[idx] = true;
|
||
|
||
for (size_t idx = 0; idx < to_merge.size(); ++idx) {
|
||
for (auto iter = to_merge[idx].begin(); iter != to_merge[idx].end();++iter) {
|
||
if (iter->z() >= subject_idx_range) {
|
||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: encounter idx from clip: %d",iter->z());
|
||
}
|
||
}
|
||
}
|
||
|
||
return merge_lines(to_merge,floating_flags,line,subject_idx_range,default_width);
|
||
}
|
||
|
||
int start_none_floating_idx(int idx, const std::vector<int>& none_floating_count)
|
||
{
|
||
int backtrace_idx = idx - none_floating_count[idx] + 1;
|
||
if (backtrace_idx >= 0)
|
||
return backtrace_idx;
|
||
else
|
||
return none_floating_count.size() + backtrace_idx;
|
||
}
|
||
|
||
template<typename PointContainer>
|
||
void get_none_floating_prefix(const PointContainer& container, const ExPolygons& floating_areas, const Polygons& sparse_polys, std::vector<double>& none_floating_length, std::vector<int>& none_floating_count)
|
||
{
|
||
std::vector<double>(container.points.size(), 0).swap(none_floating_length);
|
||
std::vector<int>(container.points.size(), 0).swap(none_floating_count);
|
||
|
||
std::vector<BoundingBox> floating_bboxs;
|
||
for (size_t idx = 0; idx < floating_areas.size(); ++idx)
|
||
floating_bboxs.emplace_back(get_extents(floating_areas[idx]));
|
||
std::vector<BoundingBox> sparse_bboxs;
|
||
for (size_t idx = 0; idx < sparse_polys.size(); ++idx)
|
||
sparse_bboxs.emplace_back(get_extents(sparse_polys[idx]));
|
||
|
||
auto point_in_floating_area = [&floating_bboxs, &sparse_bboxs, &floating_areas, &sparse_polys](const Point& p)->bool {
|
||
for (size_t idx = 0; idx < sparse_polys.size(); ++idx) {
|
||
if (!sparse_bboxs[idx].contains(p))
|
||
continue;
|
||
if (sparse_polys[idx].contains(p))
|
||
return false;
|
||
}
|
||
for (size_t idx = 0; idx < floating_areas.size(); ++idx) {
|
||
if (!floating_bboxs[idx].contains(p))
|
||
continue;
|
||
if (floating_areas[idx].contains(p))
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
for (size_t idx = 0; idx < container.points.size(); ++idx) {
|
||
const Point& p = container.points[idx];
|
||
if (!point_in_floating_area(p)) {
|
||
if (idx == 0)
|
||
none_floating_count[idx] = 1;
|
||
else
|
||
none_floating_count[idx] = none_floating_count[idx - 1] + 1;
|
||
if (none_floating_count[idx] > 1)
|
||
none_floating_length[idx] = none_floating_length[idx - 1] + ((Point)(container.points[prev_idx_modulo(idx, container.points)] - p)).cast<double>().norm();
|
||
else
|
||
none_floating_length[idx] = 0;
|
||
}
|
||
else {
|
||
none_floating_length[idx] = 0;
|
||
none_floating_count[idx] = 0;
|
||
}
|
||
}
|
||
|
||
if (none_floating_count.back() > 0) {
|
||
for (size_t idx = 0; idx < container.points.size(); ++idx) {
|
||
if (none_floating_count[idx] == 0)
|
||
break;
|
||
none_floating_count[idx] = none_floating_count[prev_idx_modulo(idx, container.points)] + 1;
|
||
none_floating_length[idx] = none_floating_length[prev_idx_modulo(idx, container.points)] + ((Point)(container.points[prev_idx_modulo(idx, container.points)] - container.points[idx])).cast<double>().norm();
|
||
}
|
||
}
|
||
}
|
||
|
||
template<typename PointContainer>
|
||
int get_best_loop_start(const PointContainer& container, const ExPolygons& floating_areas, const Polygons& sparse_polys) {
|
||
std::vector<double> none_floating_length;
|
||
std::vector<int> none_floating_count;
|
||
|
||
BoundingBox floating_bbox = get_extents(floating_areas);
|
||
BoundingBox poly_bbox(container.points);
|
||
|
||
if (!poly_bbox.overlap(floating_bbox))
|
||
return 0;
|
||
|
||
Polygons clipped_sparse_polys = ClipperUtils::clip_clipper_polygons_with_subject_bbox(sparse_polys, poly_bbox);
|
||
get_none_floating_prefix(container, floating_areas, clipped_sparse_polys, none_floating_length, none_floating_count);
|
||
int best_idx = std::distance(none_floating_length.begin(), std::max_element(none_floating_length.begin(), none_floating_length.end()));
|
||
return start_none_floating_idx(best_idx, none_floating_count);
|
||
}
|
||
|
||
template<typename PointContainer>
|
||
std::vector<int> get_loop_start_candidates(const PointContainer& container, const ExPolygons& floating_areas, const Polygons& sparse_polys)
|
||
{
|
||
std::vector<double> none_floating_length;
|
||
std::vector<int> none_floating_count;
|
||
|
||
BoundingBox floating_bbox = get_extents(floating_areas);
|
||
BoundingBox poly_bbox(container.points);
|
||
std::vector<int> candidate_list;
|
||
|
||
if (!poly_bbox.overlap(floating_bbox)) {
|
||
candidate_list.resize(container.points.size());
|
||
std::iota(candidate_list.begin(), candidate_list.end(), 0);
|
||
return candidate_list;
|
||
}
|
||
Polygons clipped_sparse_polys = ClipperUtils::clip_clipper_polygons_with_subject_bbox(sparse_polys, poly_bbox);
|
||
get_none_floating_prefix(container, floating_areas, clipped_sparse_polys, none_floating_length, none_floating_count);
|
||
for (size_t idx = 0; idx < none_floating_length.size(); ++idx) {
|
||
if (none_floating_length[idx] > 0)
|
||
candidate_list.emplace_back(start_none_floating_idx(idx, none_floating_count));
|
||
}
|
||
return candidate_list;
|
||
}
|
||
|
||
|
||
void smooth_floating_line(FloatingThickPolyline& line,coord_t max_gap_threshold, coord_t min_floating_threshold)
|
||
{
|
||
if (line.empty())
|
||
return;
|
||
struct LineParts {
|
||
int start;
|
||
int end;
|
||
bool is_floating;
|
||
};
|
||
|
||
auto build_line_parts = [&](const FloatingThickPolyline& line)->std::vector<LineParts> {
|
||
std::vector<LineParts> line_parts;
|
||
bool current_val = line.is_floating.front();
|
||
int start = 0;
|
||
for (size_t idx = 1; idx < line.is_floating.size(); ++idx) {
|
||
if (line.is_floating[idx] != current_val) {
|
||
line_parts.push_back({ start,(int)(idx - 1),current_val });
|
||
current_val = line.is_floating[idx];
|
||
start = idx;
|
||
}
|
||
}
|
||
line_parts.push_back({ start,(int)(line.is_floating.size() - 1),current_val });
|
||
return line_parts;
|
||
};
|
||
|
||
std::vector<double> distance_prefix(line.points.size(),0);
|
||
for (size_t idx = 0; idx < line.points.size();++idx) {
|
||
if (idx == 0)
|
||
distance_prefix[idx] = 0;
|
||
else {
|
||
distance_prefix[idx] = distance_prefix[idx - 1] + (line.points[idx] - line.points[idx - 1]).cast<double>().norm();
|
||
}
|
||
}
|
||
{
|
||
// remove too small gaps
|
||
std::vector<LineParts> line_parts = build_line_parts(line);
|
||
std::vector<std::pair<int, int>> gaps_to_merge;
|
||
|
||
for (size_t i = 1; i + 1 < line_parts.size(); ++i) {
|
||
const auto& curr = line_parts[i];
|
||
if (!curr.is_floating) {
|
||
const auto& prev = line_parts[i - 1];
|
||
const auto& next = line_parts[i + 1];
|
||
if (prev.is_floating && next.is_floating) {
|
||
double total_length = distance_prefix[next.start] - distance_prefix[prev.end];
|
||
if (total_length < max_gap_threshold) {
|
||
gaps_to_merge.emplace_back(curr.start, curr.end);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for (const auto& gap : gaps_to_merge) {
|
||
for (int i = gap.first; i <= gap.second; ++i) {
|
||
line.is_floating[i] = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
{
|
||
std::vector<LineParts> line_parts = build_line_parts(line);
|
||
std::vector<std::pair<int, int>> segments_to_remove;
|
||
|
||
for (auto& part : line_parts) {
|
||
if (part.is_floating && distance_prefix[part.end] - distance_prefix[part.start] < min_floating_threshold) {
|
||
segments_to_remove.emplace_back(part.start, part.end);
|
||
}
|
||
}
|
||
|
||
for (const auto& seg : segments_to_remove) {
|
||
for (int i = seg.first; i <= seg.second; ++i) {
|
||
line.is_floating[i] = false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// nearest neibour排序,但是取点时,只能取get_loop_start_candidates得到的点
|
||
FloatingThickPolylines FillFloatingConcentric::resplit_order_loops(Point curr_point, std::vector<const Arachne::ExtrusionLine*> all_extrusions, const ExPolygons& floating_areas, const Polygons& sparse_polys, const coord_t default_width)
|
||
{
|
||
FloatingThickPolylines result;
|
||
|
||
for (size_t idx = 0; idx < all_extrusions.size(); ++idx) {
|
||
if (all_extrusions[idx]->empty())
|
||
continue;
|
||
ThickPolyline thick_polyline = Arachne::to_thick_polyline(*all_extrusions[idx]);
|
||
FloatingThickPolyline thick_line_with_floating = detect_floating_line(thick_polyline, floating_areas, default_width, !print_object_config->detect_floating_vertical_shell.value);
|
||
smooth_floating_line(thick_line_with_floating, scale_(2), scale_(2));
|
||
int split_idx = 0;
|
||
if (!floating_areas.empty() && all_extrusions[idx]->is_closed && thick_line_with_floating.points.front() == thick_line_with_floating.points.back()) {
|
||
if (idx == 0)
|
||
split_idx = get_best_loop_start(thick_line_with_floating, floating_areas,sparse_polys);
|
||
else {
|
||
auto candidates = get_loop_start_candidates(thick_line_with_floating, floating_areas,sparse_polys);
|
||
double min_dist = std::numeric_limits<double>::max();
|
||
for (auto candidate : candidates) {
|
||
double dist = (curr_point - thick_line_with_floating.points[candidate]).cast<double>().norm();
|
||
if (min_dist > dist) {
|
||
min_dist = dist;
|
||
split_idx = candidate;
|
||
}
|
||
}
|
||
}
|
||
FloatingThickPolyline new_line = thick_line_with_floating.rebase_at(split_idx);
|
||
assert(new_line.width.size() == 2 * new_line.points.size() - 2);
|
||
result.emplace_back(thick_line_with_floating.rebase_at(split_idx));
|
||
}
|
||
else {
|
||
assert(thick_line_with_floating.width.size() == 2 * thick_line_with_floating.points.size() - 2);
|
||
result.emplace_back(thick_line_with_floating);
|
||
}
|
||
curr_point = result.back().last_point();
|
||
}
|
||
return result;
|
||
}
|
||
|
||
#if 0
|
||
Polylines FillFloatingConcentric::resplit_order_loops(Point curr_point, Polygons loops, const ExPolygons& floating_areas)
|
||
{
|
||
Polylines result;
|
||
for (size_t idx = 0; idx < loops.size(); ++idx) {
|
||
const Polygon& loop = loops[idx];
|
||
int split_idx = 0;
|
||
if (!floating_areas.empty()) {
|
||
if (idx == 0)
|
||
split_idx = get_best_loop_start(loop, floating_areas);
|
||
else {
|
||
auto candidates = get_loop_start_candidates(loop, floating_areas);
|
||
double min_dist = std::numeric_limits<double>::max();
|
||
for (auto candidate : candidates) {
|
||
double dist = (curr_point - loop.points[candidate]).cast<double>().norm();
|
||
if (min_dist > dist) {
|
||
min_dist = dist;
|
||
split_idx = candidate;
|
||
}
|
||
}
|
||
}
|
||
result.emplace_back(loop.split_at_index(split_idx));
|
||
}
|
||
else {
|
||
result.emplace_back(loop.split_at_index(curr_point.nearest_point_index(loop.points)));
|
||
}
|
||
curr_point = result.back().last_point();
|
||
}
|
||
return result;
|
||
};
|
||
|
||
void FillFloatingConcentric::_fill_surface_single(
|
||
const FillParams& params,
|
||
unsigned int thickness_layers,
|
||
const std::pair<float, Point>& direction,
|
||
ExPolygon expolygon,
|
||
FloatingLines& polylines_out
|
||
)
|
||
{
|
||
auto expoly_bbox = get_extents(expolygon);
|
||
coord_t min_spacing = scale_(this->spacing);
|
||
coord_t distance = coord_t(min_spacing / params.density);
|
||
distance = this->_adjust_solid_spacing(expoly_bbox.size()(0), distance);
|
||
this->spacing = unscale<double>(distance);
|
||
|
||
Polygons loops = to_polygons(expolygon);
|
||
ExPolygons offseted_expolys{ std::move(expolygon) };
|
||
while (!offseted_expolys.empty()) {
|
||
offseted_expolys = offset2_ex(offseted_expolys, -(distance + min_spacing / 2), min_spacing / 2);
|
||
append(loops, to_polygons(offseted_expolys));
|
||
}
|
||
// generate paths from outermost to the inner most
|
||
loops = union_pt_chained_outside_in(loops);
|
||
|
||
auto reordered_polylines = resplit_order_loops({ 0,0 }, loops, lower_layer_unsupport_areas);
|
||
size_t i = polylines_out.size();
|
||
for (auto& polyline : reordered_polylines) {
|
||
polylines_out.emplace_back(std::move(polyline));
|
||
}
|
||
|
||
size_t j = polylines_out.size();
|
||
for (; i < polylines_out.size(); ++i) {
|
||
polylines_out[i].clip_end(this->loop_clipping);
|
||
if (polylines_out[i].is_valid()) {
|
||
if (j < i)
|
||
polylines_out[j] = std::move(polylines_out[i]);
|
||
++j;
|
||
}
|
||
}
|
||
if (j < polylines_out.size())
|
||
polylines_out.erase(polylines_out.begin() + j, polylines_out.end());
|
||
}
|
||
#endif
|
||
|
||
void FillFloatingConcentric::_fill_surface_single(const FillParams& params,
|
||
unsigned int thickness_layers,
|
||
const std::pair<float, Point>& direction,
|
||
ExPolygon expolygon,
|
||
FloatingThickPolylines& thick_polylines_out)
|
||
{
|
||
Point bbox_size = expolygon.contour.bounding_box().size();
|
||
coord_t min_spacing = params.flow.scaled_spacing();
|
||
|
||
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
|
||
Polygons polygons = to_polygons(expolygon);
|
||
|
||
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
|
||
Arachne::WallToolPathsParams input_params;
|
||
input_params.min_bead_width = 0.85 * min_nozzle_diameter;
|
||
input_params.min_feature_size = 0.25 * min_nozzle_diameter;
|
||
input_params.wall_transition_length = 0.4;
|
||
input_params.wall_transition_angle = 10;
|
||
input_params.wall_transition_filter_deviation = 0.25 * min_nozzle_diameter;
|
||
input_params.wall_distribution_count = 1;
|
||
|
||
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, params.layer_height, input_params);
|
||
|
||
std::vector<Arachne::VariableWidthLines> loops = wallToolPaths.getToolPaths();
|
||
std::vector<const Arachne::ExtrusionLine*> all_extrusions;
|
||
for (Arachne::VariableWidthLines& loop : loops) {
|
||
if (loop.empty())
|
||
continue;
|
||
for (const Arachne::ExtrusionLine& wall : loop)
|
||
all_extrusions.emplace_back(&wall);
|
||
}
|
||
|
||
// Split paths using a nearest neighbor search.
|
||
size_t firts_poly_idx = thick_polylines_out.size();
|
||
auto thick_polylines = resplit_order_loops({ 0,0 }, all_extrusions, this->lower_layer_unsupport_areas, this->lower_sparse_polys,min_spacing);
|
||
append(thick_polylines_out, thick_polylines);
|
||
|
||
|
||
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
|
||
// Keep valid paths only.
|
||
size_t j = firts_poly_idx;
|
||
for (size_t i = firts_poly_idx; i < thick_polylines_out.size(); ++i) {
|
||
thick_polylines_out[i].clip_end(this->loop_clipping);
|
||
if (thick_polylines_out[i].is_valid()) {
|
||
if (j < i)
|
||
thick_polylines_out[j] = std::move(thick_polylines_out[i]);
|
||
++j;
|
||
}
|
||
}
|
||
if (j < thick_polylines_out.size())
|
||
thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end());
|
||
}
|
||
|
||
|
||
FloatingThickPolylines FillFloatingConcentric::fill_surface_arachne_floating(const Surface* surface, const FillParams& params)
|
||
{
|
||
// Create the infills for each of the regions.
|
||
FloatingThickPolylines floating_thick_polylines_out;
|
||
for (ExPolygon& expoly : no_overlap_expolygons)
|
||
_fill_surface_single(params, surface->thickness_layers, _infill_direction(surface), std::move(expoly), floating_thick_polylines_out);
|
||
return floating_thick_polylines_out;
|
||
}
|
||
|
||
void FillFloatingConcentric::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out)
|
||
{
|
||
FloatingThickPolylines floating_lines = this->fill_surface_arachne_floating(surface, params);
|
||
if (floating_lines.empty())
|
||
return;
|
||
Flow new_flow = params.flow.with_spacing(this->spacing);
|
||
double flow_mm3_per_mm = new_flow.mm3_per_mm();
|
||
double flow_width = new_flow.width();
|
||
|
||
ExtrusionEntityCollection* ecc = new ExtrusionEntityCollection();
|
||
ecc->no_sort = true;
|
||
out.push_back(ecc);
|
||
size_t idx = ecc->entities.size();
|
||
|
||
const float tolerance = float(scale_(0.05));
|
||
for (const auto& line : floating_lines) {
|
||
ExtrusionPaths paths = floating_thick_polyline_to_extrusion_paths(line, params.extrusion_role, new_flow, tolerance);
|
||
// Append paths to collection.
|
||
assert(!paths.empty());
|
||
if (!paths.empty()) {
|
||
if (paths.front().first_point() == paths.back().last_point())
|
||
ecc->entities.emplace_back(new ExtrusionLoop(std::move(paths)));
|
||
else {
|
||
for (ExtrusionPath& path : paths) {
|
||
assert(!path.empty());
|
||
ecc->entities.emplace_back(new ExtrusionPath(std::move(path)));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
} // namespace Slic3r
|