mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-20 06:01:16 +00:00
* Add test for Arachne duplicate wall segment detection Add test cases that reproduce an issue where Arachne generates duplicate/coinciding extrusion segments at certain min_bead_width settings. Test configuration: - Profile: 0.28mm Extra Draft @BBL X1C (0.4mm nozzle, 0.28mm layer) - outer_wall_line_width: 0.42mm, inner_wall_line_width: 0.45mm - wall_loops: 2, precise_outer_wall: enabled - Test polygon: outer rectangle (0,0)-(20,20) with inner cutout (0.5,0.5)-(19.5,19.5) This creates a 0.5mm wide frame around the perimeter. Results: - 50% min_bead_width (0.20mm): FAILS - detects 4 duplicate segments (all 4 sides) - 60% min_bead_width (0.24mm): PASSES - no duplicates At 50%, Arachne generates two separate closed loops that share all 4 edges of the inner square. At 60%, Arachne generates a single closed loop. SVG output is exported to /tmp/opencode/ for visual debugging. * Fix Arachne duplicate extrusion caused by bead count mismatch WideningBeadingStrategy::compute() used optimal_width (inner wall width) to determine if a thin wall should produce a single bead. However, getOptimalBeadCount() uses optimal_width_outer (outer wall width) via RedistributeBeadingStrategy to decide the bead count. This inconsistency caused situations where getOptimalBeadCount() returned 2 beads, but compute() produced only 1 bead at full thickness. The single bead was then generated for both inner and outer contours, resulting in duplicate extrusion paths. Fix: Use getTransitionThickness(1) instead of optimal_width. This method returns the exact threshold for the 1-to-2 bead transition, ensuring consistency between bead count calculation and bead generation. Reproduces with: 50% min_bead_width, 0.42mm outer wall, 0.45mm inner wall, 0.5mm polygon inset creating ~0.38mm wall thickness. Fixes #13917 --------- Co-authored-by: SoftFever <softfeverever@gmail.com>
93 lines
3.0 KiB
C++
93 lines
3.0 KiB
C++
//Copyright (c) 2022 Ultimaker B.V.
|
|
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
|
|
|
#include "WideningBeadingStrategy.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include "libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp"
|
|
|
|
namespace Slic3r::Arachne
|
|
{
|
|
|
|
WideningBeadingStrategy::WideningBeadingStrategy(BeadingStrategyPtr parent, const coord_t min_input_width, const coord_t min_output_width)
|
|
: BeadingStrategy(*parent)
|
|
, parent(std::move(parent))
|
|
, min_input_width(min_input_width)
|
|
, min_output_width(min_output_width)
|
|
{
|
|
}
|
|
|
|
std::string WideningBeadingStrategy::toString() const
|
|
{
|
|
return std::string("Widening+") + parent->toString();
|
|
}
|
|
|
|
WideningBeadingStrategy::Beading WideningBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const
|
|
{
|
|
// Use getTransitionThickness(1) to determine if this is a thin wall that should produce
|
|
// a single bead. This ensures consistency with getOptimalBeadCount() which uses the same
|
|
// threshold (via RedistributeBeadingStrategy) to decide between 1 and 2 beads.
|
|
// Previously used optimal_width which could differ from the outer wall width used in
|
|
// bead count calculations, causing inconsistency where bead_count=2 was requested but
|
|
// only 1 bead was produced.
|
|
if (thickness < getTransitionThickness(1)) {
|
|
Beading ret;
|
|
ret.total_thickness = thickness;
|
|
if (thickness >= min_input_width) {
|
|
ret.bead_widths.emplace_back(std::max(thickness, min_output_width));
|
|
ret.toolpath_locations.emplace_back(thickness / 2);
|
|
ret.left_over = 0;
|
|
} else
|
|
ret.left_over = thickness;
|
|
|
|
return ret;
|
|
} else
|
|
return parent->compute(thickness, bead_count);
|
|
}
|
|
|
|
coord_t WideningBeadingStrategy::getOptimalThickness(coord_t bead_count) const
|
|
{
|
|
return parent->getOptimalThickness(bead_count);
|
|
}
|
|
|
|
coord_t WideningBeadingStrategy::getTransitionThickness(coord_t lower_bead_count) const
|
|
{
|
|
if (lower_bead_count == 0)
|
|
return min_input_width;
|
|
else
|
|
return parent->getTransitionThickness(lower_bead_count);
|
|
}
|
|
|
|
coord_t WideningBeadingStrategy::getOptimalBeadCount(coord_t thickness) const
|
|
{
|
|
if (thickness < min_input_width)
|
|
return 0;
|
|
coord_t ret = parent->getOptimalBeadCount(thickness);
|
|
if (thickness >= min_input_width && ret < 1)
|
|
return 1;
|
|
return ret;
|
|
}
|
|
|
|
coord_t WideningBeadingStrategy::getTransitioningLength(coord_t lower_bead_count) const
|
|
{
|
|
return parent->getTransitioningLength(lower_bead_count);
|
|
}
|
|
|
|
float WideningBeadingStrategy::getTransitionAnchorPos(coord_t lower_bead_count) const
|
|
{
|
|
return parent->getTransitionAnchorPos(lower_bead_count);
|
|
}
|
|
|
|
std::vector<coord_t> WideningBeadingStrategy::getNonlinearThicknesses(coord_t lower_bead_count) const
|
|
{
|
|
std::vector<coord_t> ret;
|
|
ret.emplace_back(min_output_width);
|
|
std::vector<coord_t> pret = parent->getNonlinearThicknesses(lower_bead_count);
|
|
ret.insert(ret.end(), pret.begin(), pret.end());
|
|
return ret;
|
|
}
|
|
|
|
} // namespace Slic3r::Arachne
|