SPE-2615: Fix missing layers and printing in the air on tiny parts with Arachne.

Fix adapted from CuraEngine.

Co-authored-by: ThomasRahm <ThomasRahm@users.noreply.github.com>
Co-authored-by: rburema <r.burema@ultimaker.com>
This commit is contained in:
Lukáš Hejl 2025-01-04 22:53:08 +01:00 committed by Lukas Matena
parent f530108a81
commit 671d0c9b3d
4 changed files with 81 additions and 31 deletions

View File

@ -2054,36 +2054,79 @@ void SkeletalTrapezoidation::generateLocalMaximaSingleBeads()
{ {
std::vector<VariableWidthLines> &generated_toolpaths = *p_generated_toolpaths; std::vector<VariableWidthLines> &generated_toolpaths = *p_generated_toolpaths;
for (auto& node : graph.nodes) const auto add_circle_to_toolpath = [&generated_toolpaths](const Point &center, const coord_t width, const size_t inset_index) -> void {
{ constexpr bool is_odd = true;
if (! node.data.hasBeading())
{ if (inset_index >= generated_toolpaths.size()) {
generated_toolpaths.resize(inset_index + 1);
}
generated_toolpaths[inset_index].emplace_back(inset_index, is_odd);
ExtrusionLine &line = generated_toolpaths[inset_index].back();
// total area to be extruded is pi*(w/2)^2 = pi*w*w/4
// Width a constant extrusion width w, that would be a length of pi*w/4
// If we make a small circle to fill up the hole, then that circle would have a circumference of 2*pi*r
// So our circle needs to be such that r=w/8
const coord_t radius = width / 8;
constexpr coord_t n_segments = 6;
for (coord_t segment = 0; segment < n_segments; segment++) {
double a = 2. * M_PI / n_segments * segment;
line.junctions.emplace_back(center + Point(radius * cos(a), radius * sin(a)), width, inset_index);
}
};
const auto should_replace_with_local_maxima = [&generated_toolpaths]() -> bool {
if (generated_toolpaths.empty() || generated_toolpaths[0].empty()) {
return true;
}
coord_t total_path_length = 0;
coord_t min_width = std::numeric_limits<coord_t>::max();
for (const auto &line : generated_toolpaths[0]) {
total_path_length += static_cast<coord_t>(line.getLength());
for (const ExtrusionJunction &j : line) {
min_width = std::min(min_width, j.w);
}
}
return total_path_length <= (min_width / 2);
};
Vec2i64 local_maxima_accumulator = Vec2i64::Zero();
coord_t width_accumulator = 0;
size_t accumulator_count = 0;
for (const auto &node : graph.nodes) {
if (!node.data.hasBeading()) {
continue; continue;
} }
Beading& beading = node.data.getBeading()->beading;
if (beading.bead_widths.size() % 2 == 1 && node.isLocalMaximum(true) && !node.isCentral()) const Beading &beading = node.data.getBeading()->beading;
{ if (beading.bead_widths.size() % 2 == 1 && node.isLocalMaximum(true)) {
const size_t inset_index = beading.bead_widths.size() / 2; const size_t inset_index = beading.bead_widths.size() / 2;
constexpr bool is_odd = true; const coord_t width = beading.bead_widths[inset_index];
if (inset_index >= generated_toolpaths.size())
{ local_maxima_accumulator += node.p.cast<int64_t>();
generated_toolpaths.resize(inset_index + 1); width_accumulator += width;
} ++accumulator_count;
generated_toolpaths[inset_index].emplace_back(inset_index, is_odd);
ExtrusionLine& line = generated_toolpaths[inset_index].back(); if (!node.isCentral()) {
const coord_t width = beading.bead_widths[inset_index]; add_circle_to_toolpath(node.p, width, inset_index);
// total area to be extruded is pi*(w/2)^2 = pi*w*w/4
// Width a constant extrusion width w, that would be a length of pi*w/4
// If we make a small circle to fill up the hole, then that circle would have a circumference of 2*pi*r
// So our circle needs to be such that r=w/8
const coord_t r = width / 8;
constexpr coord_t n_segments = 6;
for (coord_t segment = 0; segment < n_segments; segment++) {
float a = 2.0 * M_PI / n_segments * segment;
line.junctions.emplace_back(node.p + Point(r * cos(a), r * sin(a)), width, inset_index);
} }
} }
} }
if (accumulator_count > 0 && should_replace_with_local_maxima()) {
if (generated_toolpaths.empty()) {
generated_toolpaths.emplace_back();
} else {
generated_toolpaths[0].clear();
}
const coord_t width = static_cast<coord_t>(width_accumulator / accumulator_count);
const Point center = (local_maxima_accumulator / accumulator_count).cast<coord_t>();
add_circle_to_toolpath(center, width, 0);
}
} }
// //

View File

@ -547,7 +547,7 @@ protected:
void connectJunctions(ptr_vector_t<LineJunctions>& edge_junctions); void connectJunctions(ptr_vector_t<LineJunctions>& edge_junctions);
/*! /*!
* Genrate small segments for local maxima where the beading would only result in a single bead * Generate small segments for local maxima where the beading would only result in a single bead
*/ */
void generateLocalMaximaSingleBeads(); void generateLocalMaximaSingleBeads();
}; };

View File

@ -46,7 +46,8 @@ public:
{ {
beading = storage; beading = storage;
} }
std::shared_ptr<BeadingPropagation> getBeading()
std::shared_ptr<BeadingPropagation> getBeading() const
{ {
return beading.lock(); return beading.lock();
} }

View File

@ -658,11 +658,17 @@ template<typename T> bool shorterThan(const T &shape, const coord_t check_length
void WallToolPaths::removeSmallLines(std::vector<VariableWidthLines> &toolpaths) void WallToolPaths::removeSmallLines(std::vector<VariableWidthLines> &toolpaths)
{ {
for (VariableWidthLines &inset : toolpaths) { for (VariableWidthLines &inset : toolpaths) {
for (size_t line_idx = 0; line_idx < inset.size(); line_idx++) { for (size_t line_idx = 0; line_idx < inset.size(); ++line_idx) {
ExtrusionLine &line = inset[line_idx]; ExtrusionLine &line = inset[line_idx];
coord_t min_width = std::numeric_limits<coord_t>::max(); if (line.is_external_perimeter()) {
for (const ExtrusionJunction &j : line) continue;
}
coord_t min_width = std::numeric_limits<coord_t>::max();
for (const ExtrusionJunction &j : line) {
min_width = std::min(min_width, j.w); min_width = std::min(min_width, j.w);
}
if (line.is_odd && !line.is_closed && shorterThan(line, min_width / 2)) { // remove line if (line.is_odd && !line.is_closed && shorterThan(line, min_width / 2)) { // remove line
line = std::move(inset.back()); line = std::move(inset.back());
inset.erase(--inset.end()); inset.erase(--inset.end());