mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 16:57:53 -06:00
Change direction for perimeter extrusion at odd layers. (#2413)
* Change direction for perimeter extrusion at odd layers. Initial test code based on supermerill/SuperSlicer@87245ae3c1 * Perimeters for both contours and holes can have alternating direction, to support overhangs on both side. Also fixes wipe on loop. * Only reverse order if the loops have steep overhang * Support a special case that treat every layer as steep overhang * Add options * Disable overhang reverse if vase mode is enabled * Allow extrusion reversal and skip overhang detection if fuzzy skin --------- Co-authored-by: supermerill <merill@free.fr> Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
bbfb9d77fa
commit
951252c597
12 changed files with 199 additions and 35 deletions
|
@ -43,10 +43,13 @@ enum ExtrusionRole : uint8_t {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Special flags describing loop
|
// Special flags describing loop
|
||||||
enum ExtrusionLoopRole {
|
enum ExtrusionLoopRole : uint8_t {
|
||||||
elrDefault,
|
elrDefault=0x0,
|
||||||
elrContourInternalPerimeter,
|
// Loop for the hole, not for the contour
|
||||||
elrSkirt,
|
elrHole=0x1,
|
||||||
|
// Loop that is the most closest to infill
|
||||||
|
elrInternal = 0x2,
|
||||||
|
elrSkirt=0x4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4152,8 +4152,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||||
// next copies (if any) would not detect the correct orientation
|
// next copies (if any) would not detect the correct orientation
|
||||||
|
|
||||||
// extrude all loops ccw
|
bool is_hole = (loop.loop_role() & elrHole) == elrHole;
|
||||||
bool was_clockwise = loop.make_counter_clockwise();
|
|
||||||
|
if (m_config.spiral_mode && !is_hole) {
|
||||||
|
// if spiral vase, we have to ensure that all contour are in the same orientation.
|
||||||
|
loop.make_counter_clockwise();
|
||||||
|
}
|
||||||
|
|
||||||
// find the point of the loop that is closest to the current extruder position
|
// find the point of the loop that is closest to the current extruder position
|
||||||
// or randomize if requested
|
// or randomize if requested
|
||||||
|
@ -4210,26 +4214,20 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||||
// make a little move inwards before leaving loop
|
// make a little move inwards before leaving loop
|
||||||
if (m_config.wipe_on_loops.value && paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.wall_loops.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) {
|
if (m_config.wipe_on_loops.value && paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.wall_loops.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) {
|
||||||
// detect angle between last and first segment
|
// detect angle between last and first segment
|
||||||
// the side depends on the original winding order of the polygon (left for contours, right for holes)
|
// the side depends on the original winding order of the polygon (inwards for contours, outwards for holes)
|
||||||
//FIXME improve the algorithm in case the loop is tiny.
|
//FIXME improve the algorithm in case the loop is tiny.
|
||||||
//FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query).
|
//FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query).
|
||||||
Point a = paths.front().polyline.points[1]; // second point
|
Point a = paths.front().polyline.points[1]; // second point
|
||||||
Point b = *(paths.back().polyline.points.end()-3); // second to last point
|
Point b = *(paths.back().polyline.points.end()-3); // second to last point
|
||||||
if (was_clockwise) {
|
if (is_hole == loop.is_counter_clockwise()) {
|
||||||
// swap points
|
// swap points
|
||||||
Point c = a; a = b; b = c;
|
Point c = a; a = b; b = c;
|
||||||
|
|
||||||
// double angle = paths.front().first_point().ccw_angle(a, b) / 3;
|
|
||||||
|
|
||||||
// // turn left if contour, turn right if hole
|
|
||||||
// if (was_clockwise) angle *= -1;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double angle = paths.front().first_point().ccw_angle(a, b) / 3;
|
double angle = paths.front().first_point().ccw_angle(a, b) / 3;
|
||||||
|
|
||||||
// turn left if contour, turn right if hole
|
// turn inwards if contour, turn outwards if hole
|
||||||
if (was_clockwise) angle *= -1;
|
if (is_hole == loop.is_counter_clockwise()) angle *= -1;
|
||||||
|
|
||||||
// create the destination point along the first segment and rotate it
|
// create the destination point along the first segment and rotate it
|
||||||
// we make sure we don't exceed the segment length because we don't know
|
// we make sure we don't exceed the segment length because we don't know
|
||||||
|
|
|
@ -174,6 +174,8 @@ void Layer::make_perimeters()
|
||||||
&& config.gap_infill_speed.value == other_config.gap_infill_speed.value
|
&& config.gap_infill_speed.value == other_config.gap_infill_speed.value
|
||||||
&& config.filter_out_gap_fill.value == other_config.filter_out_gap_fill.value
|
&& config.filter_out_gap_fill.value == other_config.filter_out_gap_fill.value
|
||||||
&& config.detect_overhang_wall == other_config.detect_overhang_wall
|
&& config.detect_overhang_wall == other_config.detect_overhang_wall
|
||||||
|
&& config.overhang_reverse == other_config.overhang_reverse
|
||||||
|
&& config.overhang_reverse_threshold == other_config.overhang_reverse_threshold
|
||||||
&& config.opt_serialize("inner_wall_line_width") == other_config.opt_serialize("inner_wall_line_width")
|
&& config.opt_serialize("inner_wall_line_width") == other_config.opt_serialize("inner_wall_line_width")
|
||||||
&& config.opt_serialize("outer_wall_line_width") == other_config.opt_serialize("outer_wall_line_width")
|
&& config.opt_serialize("outer_wall_line_width") == other_config.opt_serialize("outer_wall_line_width")
|
||||||
&& config.detect_thin_wall == other_config.detect_thin_wall
|
&& config.detect_thin_wall == other_config.detect_thin_wall
|
||||||
|
|
|
@ -223,12 +223,61 @@ static void lowpass_filter_by_paths_overhang_degree(ExtrusionPaths& paths) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
|
template<class _T>
|
||||||
|
static bool detect_steep_overhang(const PrintRegionConfig *config,
|
||||||
|
bool is_contour,
|
||||||
|
const BoundingBox &extrusion_bboxs,
|
||||||
|
double extrusion_width,
|
||||||
|
const _T extrusion,
|
||||||
|
const ExPolygons *lower_slices,
|
||||||
|
bool &steep_overhang_contour,
|
||||||
|
bool &steep_overhang_hole)
|
||||||
|
{
|
||||||
|
double threshold = config->overhang_reverse_threshold.get_abs_value(extrusion_width);
|
||||||
|
// Special case: reverse on every odd layer
|
||||||
|
if (threshold < EPSILON) {
|
||||||
|
if (is_contour) {
|
||||||
|
steep_overhang_contour = true;
|
||||||
|
} else {
|
||||||
|
steep_overhang_hole = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Polygons lower_slcier_chopped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, extrusion_bboxs, true);
|
||||||
|
|
||||||
|
// All we need to check is whether we have lines outside `threshold`
|
||||||
|
double off = threshold - 0.5 * extrusion_width;
|
||||||
|
|
||||||
|
auto limiton_polygons = offset(lower_slcier_chopped, float(scale_(off)));
|
||||||
|
|
||||||
|
auto remain_polylines = diff_pl(extrusion, limiton_polygons);
|
||||||
|
if (!remain_polylines.empty()) {
|
||||||
|
if (is_contour) {
|
||||||
|
steep_overhang_contour = true;
|
||||||
|
} else {
|
||||||
|
steep_overhang_hole = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls,
|
||||||
|
bool &steep_overhang_contour, bool &steep_overhang_hole)
|
||||||
{
|
{
|
||||||
// loops is an arrayref of ::Loop objects
|
// loops is an arrayref of ::Loop objects
|
||||||
// turn each one into an ExtrusionLoop object
|
// turn each one into an ExtrusionLoop object
|
||||||
ExtrusionEntityCollection coll;
|
ExtrusionEntityCollection coll;
|
||||||
Polygon fuzzified;
|
Polygon fuzzified;
|
||||||
|
|
||||||
|
// Detect steep overhangs
|
||||||
|
bool overhangs_reverse = perimeter_generator.config->overhang_reverse &&
|
||||||
|
perimeter_generator.layer_id % 2 == 1; // Only calculate overhang degree on odd layers
|
||||||
|
|
||||||
for (const PerimeterGeneratorLoop &loop : loops) {
|
for (const PerimeterGeneratorLoop &loop : loops) {
|
||||||
bool is_external = loop.is_external();
|
bool is_external = loop.is_external();
|
||||||
bool is_small_width = loop.is_smaller_width_perimeter;
|
bool is_small_width = loop.is_smaller_width_perimeter;
|
||||||
|
@ -240,9 +289,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||||
// Note that we set loop role to ContourInternalPerimeter
|
// Note that we set loop role to ContourInternalPerimeter
|
||||||
// also when loop is both internal and external (i.e.
|
// also when loop is both internal and external (i.e.
|
||||||
// there's only one contour loop).
|
// there's only one contour loop).
|
||||||
loop_role = elrContourInternalPerimeter;
|
loop_role = elrInternal;
|
||||||
} else {
|
} else {
|
||||||
loop_role = elrDefault;
|
loop_role = loop.is_contour? elrDefault : elrHole;
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect overhanging/bridging perimeters
|
// detect overhanging/bridging perimeters
|
||||||
|
@ -283,6 +332,22 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||||
BoundingBox bbox(polygon.points);
|
BoundingBox bbox(polygon.points);
|
||||||
bbox.offset(SCALED_EPSILON);
|
bbox.offset(SCALED_EPSILON);
|
||||||
|
|
||||||
|
// Always reverse extrusion if use fuzzy skin: https://github.com/SoftFever/OrcaSlicer/pull/2413#issuecomment-1769735357
|
||||||
|
if (overhangs_reverse && perimeter_generator.config->fuzzy_skin != FuzzySkinType::None) {
|
||||||
|
if (loop.is_contour) {
|
||||||
|
steep_overhang_contour = true;
|
||||||
|
} else if (perimeter_generator.config->fuzzy_skin != FuzzySkinType::External) {
|
||||||
|
steep_overhang_hole = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Detect steep overhang
|
||||||
|
// Skip the check if we already found steep overhangs
|
||||||
|
bool found_steep_overhang = (loop.is_contour && steep_overhang_contour) || (!loop.is_contour && steep_overhang_hole);
|
||||||
|
if (overhangs_reverse && !found_steep_overhang) {
|
||||||
|
detect_steep_overhang(perimeter_generator.config, loop.is_contour, bbox, extrusion_width, Polygons{polygon}, perimeter_generator.lower_slices,
|
||||||
|
steep_overhang_contour, steep_overhang_hole);
|
||||||
|
}
|
||||||
|
|
||||||
Polylines remain_polines;
|
Polylines remain_polines;
|
||||||
|
|
||||||
//BBS: don't calculate overhang degree when enable fuzzy skin. It's unmeaning
|
//BBS: don't calculate overhang degree when enable fuzzy skin. It's unmeaning
|
||||||
|
@ -382,16 +447,16 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||||
} else {
|
} else {
|
||||||
const PerimeterGeneratorLoop &loop = loops[idx.first];
|
const PerimeterGeneratorLoop &loop = loops[idx.first];
|
||||||
assert(thin_walls.empty());
|
assert(thin_walls.empty());
|
||||||
ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls);
|
ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls, steep_overhang_contour, steep_overhang_hole);
|
||||||
out.entities.reserve(out.entities.size() + children.entities.size() + 1);
|
out.entities.reserve(out.entities.size() + children.entities.size() + 1);
|
||||||
ExtrusionLoop *eloop = static_cast<ExtrusionLoop*>(coll.entities[idx.first]);
|
ExtrusionLoop *eloop = static_cast<ExtrusionLoop*>(coll.entities[idx.first]);
|
||||||
coll.entities[idx.first] = nullptr;
|
coll.entities[idx.first] = nullptr;
|
||||||
|
|
||||||
|
eloop->make_counter_clockwise();
|
||||||
if (loop.is_contour) {
|
if (loop.is_contour) {
|
||||||
eloop->make_counter_clockwise();
|
|
||||||
out.append(std::move(children.entities));
|
out.append(std::move(children.entities));
|
||||||
out.entities.emplace_back(eloop);
|
out.entities.emplace_back(eloop);
|
||||||
} else {
|
} else {
|
||||||
eloop->make_clockwise();
|
|
||||||
out.entities.emplace_back(eloop);
|
out.entities.emplace_back(eloop);
|
||||||
out.append(std::move(children.entities));
|
out.append(std::move(children.entities));
|
||||||
}
|
}
|
||||||
|
@ -573,8 +638,13 @@ static void smooth_overhang_level(ExtrusionPaths &paths)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
bool &steep_overhang_contour, bool &steep_overhang_hole)
|
||||||
{
|
{
|
||||||
|
// Detect steep overhangs
|
||||||
|
bool overhangs_reverse = perimeter_generator.config->overhang_reverse &&
|
||||||
|
perimeter_generator.layer_id % 2 == 1; // Only calculate overhang degree on odd layers
|
||||||
|
|
||||||
ExtrusionEntityCollection extrusion_coll;
|
ExtrusionEntityCollection extrusion_coll;
|
||||||
for (PerimeterGeneratorArachneExtrusion& pg_extrusion : pg_extrusions) {
|
for (PerimeterGeneratorArachneExtrusion& pg_extrusion : pg_extrusions) {
|
||||||
Arachne::ExtrusionLine* extrusion = pg_extrusion.extrusion;
|
Arachne::ExtrusionLine* extrusion = pg_extrusion.extrusion;
|
||||||
|
@ -621,6 +691,41 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
||||||
extrusion_paths_append(temp_paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role,
|
extrusion_paths_append(temp_paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role,
|
||||||
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
|
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
|
||||||
|
|
||||||
|
// Always reverse extrusion if use fuzzy skin: https://github.com/SoftFever/OrcaSlicer/pull/2413#issuecomment-1769735357
|
||||||
|
if (overhangs_reverse && perimeter_generator.config->fuzzy_skin != FuzzySkinType::None) {
|
||||||
|
if (pg_extrusion.is_contour) {
|
||||||
|
steep_overhang_contour = true;
|
||||||
|
} else if (perimeter_generator.config->fuzzy_skin != FuzzySkinType::External) {
|
||||||
|
steep_overhang_hole = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Detect steep overhang
|
||||||
|
// Skip the check if we already found steep overhangs
|
||||||
|
bool found_steep_overhang = (pg_extrusion.is_contour && steep_overhang_contour) || (!pg_extrusion.is_contour && steep_overhang_hole);
|
||||||
|
if (overhangs_reverse && !found_steep_overhang) {
|
||||||
|
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)}));
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (detect_steep_overhang(perimeter_generator.config, pg_extrusion.is_contour, extrusion_bboxs, it.first, be_clipped, perimeter_generator.lower_slices,
|
||||||
|
steep_overhang_contour, steep_overhang_hole)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (perimeter_generator.config->overhang_speed_classic && perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
|
if (perimeter_generator.config->overhang_speed_classic && perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
|
||||||
|
|
||||||
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;
|
||||||
|
@ -728,13 +833,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
||||||
// Append paths to collection.
|
// Append paths to collection.
|
||||||
if (!paths.empty()) {
|
if (!paths.empty()) {
|
||||||
if (extrusion->is_closed) {
|
if (extrusion->is_closed) {
|
||||||
ExtrusionLoop extrusion_loop(std::move(paths));
|
ExtrusionLoop extrusion_loop(std::move(paths), pg_extrusion.is_contour ? elrDefault : elrHole);
|
||||||
// Restore the orientation of the extrusion loop.
|
extrusion_loop.make_counter_clockwise();
|
||||||
if (pg_extrusion.is_contour)
|
|
||||||
extrusion_loop.make_counter_clockwise();
|
|
||||||
else
|
|
||||||
extrusion_loop.make_clockwise();
|
|
||||||
|
|
||||||
for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) {
|
for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) {
|
||||||
assert(it->polyline.points.size() >= 2);
|
assert(it->polyline.points.size() >= 2);
|
||||||
assert(std::prev(it)->polyline.last_point() == it->polyline.first_point());
|
assert(std::prev(it)->polyline.last_point() == it->polyline.first_point());
|
||||||
|
@ -1303,6 +1403,23 @@ void PerimeterGenerator::apply_extra_perimeters(ExPolygons &infill_area)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reorient loop direction
|
||||||
|
static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_overhang_contour, bool steep_overhang_hole)
|
||||||
|
{
|
||||||
|
if (steep_overhang_hole || steep_overhang_contour) {
|
||||||
|
for (auto entity : entities) {
|
||||||
|
if (entity->is_loop()) {
|
||||||
|
ExtrusionLoop *eloop = static_cast<ExtrusionLoop *>(entity);
|
||||||
|
// Only reverse when needed
|
||||||
|
bool need_reverse = ((eloop->loop_role() & elrHole) == elrHole) ? steep_overhang_hole : steep_overhang_contour;
|
||||||
|
if (need_reverse) {
|
||||||
|
eloop->make_clockwise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PerimeterGenerator::process_classic()
|
void PerimeterGenerator::process_classic()
|
||||||
{
|
{
|
||||||
// other perimeters
|
// other perimeters
|
||||||
|
@ -1590,7 +1707,10 @@ void PerimeterGenerator::process_classic()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// at this point, all loops should be in contours[0]
|
// at this point, all loops should be in contours[0]
|
||||||
ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls);
|
bool steep_overhang_contour = false;
|
||||||
|
bool steep_overhang_hole = false;
|
||||||
|
ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls, steep_overhang_contour, steep_overhang_hole);
|
||||||
|
reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole);
|
||||||
|
|
||||||
// if brim will be printed, reverse the order of perimeters so that
|
// if brim will be printed, reverse the order of perimeters so that
|
||||||
// we continue inwards after having finished the brim
|
// we continue inwards after having finished the brim
|
||||||
|
@ -2108,11 +2228,13 @@ void PerimeterGenerator::process_arachne()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions); !extrusion_coll.empty())
|
bool steep_overhang_contour = false;
|
||||||
|
bool steep_overhang_hole = false;
|
||||||
|
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) {
|
||||||
|
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole);
|
||||||
this->loops->append(extrusion_coll);
|
this->loops->append(extrusion_coll);
|
||||||
|
}
|
||||||
|
|
||||||
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||||
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||||
|
|
|
@ -726,7 +726,7 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle)
|
||||||
static std::vector<std::string> s_Preset_print_options {
|
static std::vector<std::string> s_Preset_print_options {
|
||||||
"layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "slicing_mode",
|
"layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "slicing_mode",
|
||||||
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
|
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
|
||||||
"extra_perimeters_on_overhangs", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall",
|
"extra_perimeters_on_overhangs", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold",
|
||||||
"seam_position", "staggered_inner_seams", "wall_infill_order", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
|
"seam_position", "staggered_inner_seams", "wall_infill_order", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
|
||||||
"infill_direction",
|
"infill_direction",
|
||||||
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern",
|
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern",
|
||||||
|
|
|
@ -836,6 +836,27 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionBool(false));
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
|
def = this->add("overhang_reverse", coBool);
|
||||||
|
def->label = L("Reverse on odd");
|
||||||
|
def->full_label = L("Overhang reversal");
|
||||||
|
def->category = L("Quality");
|
||||||
|
def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on odd layers. This alternating pattern can drastically improve steep overhang.");
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
|
def = this->add("overhang_reverse_threshold", coFloatOrPercent);
|
||||||
|
def->label = L("Reverse threshold");
|
||||||
|
def->full_label = L("Overhang reversal threshold");
|
||||||
|
def->category = L("Quality");
|
||||||
|
def->tooltip = L("Number of mm the overhang need to be for the reversal to be considered useful. Can be a % of the perimeter width."
|
||||||
|
"\nValue 0 enables reversal on every odd layers regardless.");
|
||||||
|
def->sidetext = L("mm or %");
|
||||||
|
def->ratio_over = "line_width";
|
||||||
|
def->min = 0;
|
||||||
|
def->max_literal = 20;
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
|
||||||
|
|
||||||
def = this->add("overhang_speed_classic", coBool);
|
def = this->add("overhang_speed_classic", coBool);
|
||||||
def->label = L("Classic mode");
|
def->label = L("Classic mode");
|
||||||
def->category = L("Speed");
|
def->category = L("Speed");
|
||||||
|
|
|
@ -848,6 +848,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||||
((ConfigOptionBool, hole_to_polyhole))
|
((ConfigOptionBool, hole_to_polyhole))
|
||||||
((ConfigOptionFloatOrPercent, hole_to_polyhole_threshold))
|
((ConfigOptionFloatOrPercent, hole_to_polyhole_threshold))
|
||||||
((ConfigOptionBool, hole_to_polyhole_twisted))
|
((ConfigOptionBool, hole_to_polyhole_twisted))
|
||||||
|
((ConfigOptionBool, overhang_reverse))
|
||||||
|
((ConfigOptionFloatOrPercent, overhang_reverse_threshold))
|
||||||
)
|
)
|
||||||
|
|
||||||
PRINT_CONFIG_CLASS_DEFINE(
|
PRINT_CONFIG_CLASS_DEFINE(
|
||||||
|
|
|
@ -1095,6 +1095,8 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||||
|| opt_key == "fuzzy_skin_thickness"
|
|| opt_key == "fuzzy_skin_thickness"
|
||||||
|| opt_key == "fuzzy_skin_point_distance"
|
|| opt_key == "fuzzy_skin_point_distance"
|
||||||
|| opt_key == "detect_overhang_wall"
|
|| opt_key == "detect_overhang_wall"
|
||||||
|
|| opt_key == "overhang_reverse"
|
||||||
|
|| opt_key == "overhang_reverse_threshold"
|
||||||
//BBS
|
//BBS
|
||||||
|| opt_key == "enable_overhang_speed"
|
|| opt_key == "enable_overhang_speed"
|
||||||
|| opt_key == "detect_thin_wall"
|
|| opt_key == "detect_thin_wall"
|
||||||
|
|
|
@ -278,6 +278,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||||
! config->opt_bool("enable_support") &&
|
! config->opt_bool("enable_support") &&
|
||||||
config->opt_int("enforce_support_layers") == 0 &&
|
config->opt_int("enforce_support_layers") == 0 &&
|
||||||
! config->opt_bool("detect_thin_wall") &&
|
! config->opt_bool("detect_thin_wall") &&
|
||||||
|
! config->opt_bool("overhang_reverse") &&
|
||||||
config->opt_enum<TimelapseType>("timelapse_type") == TimelapseType::tlTraditional))
|
config->opt_enum<TimelapseType>("timelapse_type") == TimelapseType::tlTraditional))
|
||||||
{
|
{
|
||||||
wxString msg_text = _(L("Spiral mode only works when wall loops is 1, support is disabled, top shell layers is 0, sparse infill density is 0 and timelapse type is traditional."));
|
wxString msg_text = _(L("Spiral mode only works when wall loops is 1, support is disabled, top shell layers is 0, sparse infill density is 0 and timelapse type is traditional."));
|
||||||
|
@ -304,6 +305,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||||
new_conf.set_key_value("enable_support", new ConfigOptionBool(false));
|
new_conf.set_key_value("enable_support", new ConfigOptionBool(false));
|
||||||
new_conf.set_key_value("enforce_support_layers", new ConfigOptionInt(0));
|
new_conf.set_key_value("enforce_support_layers", new ConfigOptionInt(0));
|
||||||
new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false));
|
new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false));
|
||||||
|
new_conf.set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||||
new_conf.set_key_value("timelapse_type", new ConfigOptionEnum<TimelapseType>(tlTraditional));
|
new_conf.set_key_value("timelapse_type", new ConfigOptionEnum<TimelapseType>(tlTraditional));
|
||||||
sparse_infill_density = 0;
|
sparse_infill_density = 0;
|
||||||
timelapse_type = TimelapseType::tlTraditional;
|
timelapse_type = TimelapseType::tlTraditional;
|
||||||
|
@ -726,6 +728,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||||
|
|
||||||
for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" })
|
for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" })
|
||||||
toggle_line(el, config->opt_bool("hole_to_polyhole"));
|
toggle_line(el, config->opt_bool("hole_to_polyhole"));
|
||||||
|
|
||||||
|
bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall");
|
||||||
|
bool has_overhang_reverse = config->opt_bool("overhang_reverse");
|
||||||
|
bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase;
|
||||||
|
toggle_field("overhang_reverse", allow_overhang_reverse);
|
||||||
|
toggle_line("overhang_reverse_threshold", allow_overhang_reverse && has_overhang_reverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)
|
void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)
|
||||||
|
|
|
@ -8639,6 +8639,7 @@ void Plater::calib_max_vol_speed(const Calib_Params& params)
|
||||||
print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0));
|
print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0));
|
||||||
print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1));
|
print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1));
|
||||||
print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
||||||
|
print_config->set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||||
print_config->set_key_value("spiral_mode", new ConfigOptionBool(true));
|
print_config->set_key_value("spiral_mode", new ConfigOptionBool(true));
|
||||||
print_config->set_key_value("outer_wall_line_width", new ConfigOptionFloatOrPercent(line_width, false));
|
print_config->set_key_value("outer_wall_line_width", new ConfigOptionFloatOrPercent(line_width, false));
|
||||||
print_config->set_key_value("initial_layer_print_height", new ConfigOptionFloat(layer_height));
|
print_config->set_key_value("initial_layer_print_height", new ConfigOptionFloat(layer_height));
|
||||||
|
@ -8734,6 +8735,7 @@ void Plater::calib_VFA(const Calib_Params& params)
|
||||||
print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0));
|
print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0));
|
||||||
print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1));
|
print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1));
|
||||||
print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
||||||
|
print_config->set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||||
print_config->set_key_value("spiral_mode", new ConfigOptionBool(true));
|
print_config->set_key_value("spiral_mode", new ConfigOptionBool(true));
|
||||||
model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
|
model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
|
||||||
model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
|
model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
|
||||||
|
|
|
@ -1910,6 +1910,8 @@ void TabPrint::build()
|
||||||
optgroup->append_single_option_line("reduce_crossing_wall");
|
optgroup->append_single_option_line("reduce_crossing_wall");
|
||||||
optgroup->append_single_option_line("max_travel_detour_distance");
|
optgroup->append_single_option_line("max_travel_detour_distance");
|
||||||
optgroup->append_single_option_line("extra_perimeters_on_overhangs");
|
optgroup->append_single_option_line("extra_perimeters_on_overhangs");
|
||||||
|
optgroup->append_single_option_line("overhang_reverse");
|
||||||
|
optgroup->append_single_option_line("overhang_reverse_threshold");
|
||||||
|
|
||||||
page = add_options_page(L("Strength"), "empty");
|
page = add_options_page(L("Strength"), "empty");
|
||||||
optgroup = page->new_optgroup(L("Walls"), L"param_wall");
|
optgroup = page->new_optgroup(L("Walls"), L"param_wall");
|
||||||
|
|
|
@ -656,6 +656,7 @@ void CalibUtils::calib_max_vol_speed(const CalibInfo &calib_info, wxString &erro
|
||||||
print_config.set_key_value("top_shell_layers", new ConfigOptionInt(0));
|
print_config.set_key_value("top_shell_layers", new ConfigOptionInt(0));
|
||||||
print_config.set_key_value("bottom_shell_layers", new ConfigOptionInt(1));
|
print_config.set_key_value("bottom_shell_layers", new ConfigOptionInt(1));
|
||||||
print_config.set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
print_config.set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
||||||
|
print_config.set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||||
print_config.set_key_value("spiral_mode", new ConfigOptionBool(true));
|
print_config.set_key_value("spiral_mode", new ConfigOptionBool(true));
|
||||||
print_config.set_key_value("outer_wall_line_width", new ConfigOptionFloat(line_width));
|
print_config.set_key_value("outer_wall_line_width", new ConfigOptionFloat(line_width));
|
||||||
print_config.set_key_value("initial_layer_print_height", new ConfigOptionFloat(layer_height));
|
print_config.set_key_value("initial_layer_print_height", new ConfigOptionFloat(layer_height));
|
||||||
|
@ -719,6 +720,7 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message)
|
||||||
print_config.set_key_value("top_shell_layers", new ConfigOptionInt(0));
|
print_config.set_key_value("top_shell_layers", new ConfigOptionInt(0));
|
||||||
print_config.set_key_value("bottom_shell_layers", new ConfigOptionInt(1));
|
print_config.set_key_value("bottom_shell_layers", new ConfigOptionInt(1));
|
||||||
print_config.set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
print_config.set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
||||||
|
print_config.set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||||
print_config.set_key_value("spiral_mode", new ConfigOptionBool(true));
|
print_config.set_key_value("spiral_mode", new ConfigOptionBool(true));
|
||||||
model.objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
|
model.objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
|
||||||
model.objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
|
model.objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue