add “overhang_reverse”

This commit is contained in:
Wang YB
2024-05-24 10:54:29 +08:00
parent e77259c80b
commit 125abfca57
15 changed files with 318 additions and 23 deletions

View File

@@ -66,7 +66,8 @@ namespace ClipperUtils {
// Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one
// with a set of polygons covering the whole layer below.
template<typename PointsType>
inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox, PointsType &out)
//w38
inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox, PointsType &out,const bool get_entire_polygons=false)
{
using PointType = typename PointsType::value_type;
@@ -121,6 +122,8 @@ namespace ClipperUtils {
void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out)
{ clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); }
//w38
void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out, get_entire_polygons); }
void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out)
{ clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); }
@@ -149,6 +152,14 @@ namespace ClipperUtils {
return out;
}
//w38
[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, const bool get_entire_polygons)
{
Polygon out;
clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points, get_entire_polygons);
return out;
}
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox)
{
Polygons out;
@@ -172,7 +183,36 @@ namespace ClipperUtils {
out.end());
return out;
}
}
//w38
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon & src,
const BoundingBox &bbox,
const bool get_entire_polygons)
{
Polygons out;
out.reserve(src.num_contours());
out.emplace_back(clip_clipper_polygon_with_subject_bbox(src.contour, bbox, get_entire_polygons));
for (const Polygon &p : src.holes)
out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox, get_entire_polygons));
out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), out.end());
return out;
}
//w38
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons & src,
const BoundingBox &bbox,
const bool get_entire_polygons)
{
Polygons out;
out.reserve(number_polygons(src));
for (const ExPolygon &p : src) {
Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox, get_entire_polygons);
out.insert(out.end(), temp.begin(), temp.end());
}
out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), out.end());
return out;
}
}
static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree)
{

View File

@@ -326,9 +326,23 @@ namespace ClipperUtils {
[[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox);
void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out);
[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox);
//w38
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon & src,
const BoundingBox &bbox,
const bool get_entire_polygons);
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox);
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox);
}
//w38
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons & src,
const BoundingBox &bbox,
const bool get_entire_polygons);
[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon & src,
const BoundingBox &bbox,
const bool get_entire_polygons);
void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons);
}
// offset Polygons
// Wherever applicable, please use the expand() / shrink() variants instead, they convey their purpose better.

View File

@@ -115,6 +115,22 @@ Polyline ExtrusionMultiPath::as_polyline() const
}
return out;
}
//w38
bool ExtrusionLoop::make_clockwise()
{
bool was_ccw = this->polygon().is_counter_clockwise();
if (was_ccw)
this->reverse_loop();
return was_ccw;
}
bool ExtrusionLoop::make_counter_clockwise()
{
bool was_cw = this->polygon().is_clockwise();
if (was_cw)
this->reverse_loop();
return was_cw;
}
double ExtrusionLoop::area() const
{

View File

@@ -328,6 +328,9 @@ public:
double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
//w37
bool check_seam_point_angle(double angle_threshold = 0.174, double min_arm_length = 0.025) const;
//w38
bool make_clockwise();
bool make_counter_clockwise();
#ifndef NDEBUG
bool validate() const {

View File

@@ -95,9 +95,13 @@ struct ExtrusionRole : public ExtrusionRoleModifiers
// Special flags describing loop
enum ExtrusionLoopRole {
elrDefault,
elrContourInternalPerimeter,
elrSkirt,
//w38
elrDefault = 0x0,
// Loop for the hole, not for the contour
elrHole = 0x1,
// Loop that is the most closest to infill
elrInternal = 0x2,
elrSkirt = 0x4,
};
// Be careful when editing this list as many parts of the code depend

View File

@@ -2950,18 +2950,29 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
{
// Extrude all loops CCW.
bool is_hole = loop_src.is_clockwise();
//w38
//bool is_hole = loop_src.is_clockwise();
ExtrusionLoop new_loop_src = loop_src;
bool is_hole = (new_loop_src.loop_role() & elrHole) == elrHole;
//w38
if (m_config.spiral_vase && !is_hole) {
// if spiral vase, we have to ensure that all contour are in the same orientation.
new_loop_src.make_counter_clockwise();
}
Point seam_point = this->last_position.has_value() ? *this->last_position : Point::Zero();
//w37
if ((!m_config.spiral_vase && comment_is_perimeter(description)) || (this->config().seam_slope_type == SeamScarfType::None) &&comment_is_perimeter(description) ) {
assert(m_layer != nullptr);
seam_point = m_seam_placer.place_seam(m_layer, loop_src, m_config.external_perimeters_first, seam_point);
//w38
seam_point = m_seam_placer.place_seam(m_layer, new_loop_src, m_config.external_perimeters_first, seam_point);
}
// Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns,
// thus empty path segments will not be produced by G-code export.
//w38
GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam(
loop_src, is_hole, m_scaled_resolution, seam_point, scaled<double>(0.0015));
new_loop_src, is_hole, m_scaled_resolution, seam_point, scaled<double>(0.0015));
// Clip the path to avoid the extruder to get exactly on the first point of the loop;
// if polyline was shorter than the clipping distance we'd get a null polyline, so
@@ -2975,7 +2986,8 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping));
// Apply the small perimeter speed.
if (loop_src.paths.front().role().is_perimeter() && loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1)
//w38
if (new_loop_src.paths.front().role().is_perimeter() && new_loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1)
speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed);
// Extrude along the smooth path.
@@ -2985,7 +2997,8 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
double ext_length = 0;
double inner_length = 0;
double start_height = 0;
bool enable_seam_slope = loop_src.check_seam_point_angle(m_config.scarf_angle_threshold.value * M_PI / 180.0);
//w38
bool enable_seam_slope = new_loop_src.check_seam_point_angle(m_config.scarf_angle_threshold.value * M_PI / 180.0);
if (this->config().seam_slope_start_height.percent)
start_height = this->config().seam_slope_start_height * this->config().layer_height / 100;
else
@@ -3077,7 +3090,8 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
// Wipe will hide the seam.
m_wipe.set_path(std::move(smooth_path));
} else if (loop_src.paths.back().role().is_external_perimeter() && m_layer != nullptr && m_config.perimeters.value > 1) {
}//w38
else if (new_loop_src.paths.back().role().is_external_perimeter() && m_layer != nullptr && m_config.perimeters.value > 1) {
// Only wipe inside if the wipe along the perimeter is disabled.
// Make a little move inwards before leaving loop.

View File

@@ -683,7 +683,10 @@ void Layer::make_perimeters()
&& config.infill_overlap == other_config.infill_overlap
&& config.fuzzy_skin == other_config.fuzzy_skin
&& config.fuzzy_skin_thickness == other_config.fuzzy_skin_thickness
&& config.fuzzy_skin_point_dist == other_config.fuzzy_skin_point_dist)
&& config.fuzzy_skin_point_dist == other_config.fuzzy_skin_point_dist
//w38
&& config.overhang_reverse == other_config.overhang_reverse
&& config.overhang_reverse_threshold == other_config.overhang_reverse_threshold)
{
layer_region_reset_perimeters(*other_layerm);
layer_region_ids.push_back(it - m_regions.begin());

View File

@@ -114,6 +114,8 @@ void LayerRegion::make_perimeters(
Polygons lower_layer_polygons_cache;
//w16
Polygons upper_layer_polygons_cache;
//w38
params.lower_slices = lower_slices;
for (const Surface &surface : slices) {
auto perimeters_begin = uint32_t(m_perimeters.size());

View File

@@ -285,12 +285,62 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine &ext_lines, double fuzzy
using PerimeterGeneratorLoops = std::vector<PerimeterGeneratorLoop>;
static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator::Parameters &params, const Polygons &lower_slices_polygons_cache, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
//w38
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;
}
//w38
static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator::Parameters &params, const Polygons &lower_slices_polygons_cache, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls,
bool & steep_overhang_contour,
bool & steep_overhang_hole)
{
// loops is an arrayref of ::Loop objects
// turn each one into an ExtrusionLoop object
ExtrusionEntityCollection coll;
Polygon fuzzified;
//w38
bool overhangs_reverse = params.config.overhang_reverse &&
params.layer_id % 2 == 1;
for (const PerimeterGeneratorLoop &loop : loops) {
bool is_external = loop.is_external();
@@ -301,13 +351,24 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator
// Note that we set loop role to ContourInternalPerimeter
// also when loop is both internal and external (i.e.
// there's only one contour loop).
loop_role = elrContourInternalPerimeter;
//w38
loop_role = elrInternal;//loop_role = elrContourInternalPerimeter;
} else {
loop_role = elrDefault;
//w38
loop_role = loop.is_contour? elrDefault : elrHole;//loop_role = elrDefault;
}
// detect overhanging/bridging perimeters
ExtrusionPaths paths;
//w38
double extrusion_width;
if (is_external) {
extrusion_width = params.ext_perimeter_flow.width();
} else {
extrusion_width = params.perimeter_flow.width();
}
const Polygon &polygon = loop.fuzzify ? fuzzified : loop.polygon;
if (loop.fuzzify) {
fuzzified = loop.polygon;
@@ -318,6 +379,19 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator
params.object_config.support_material_contact_distance.value == 0)) {
BoundingBox bbox(polygon.points);
bbox.offset(SCALED_EPSILON);
//w38
if (overhangs_reverse && params.config.fuzzy_skin != FuzzySkinType::None) {
if (loop.is_contour) {
steep_overhang_contour = true;
} else if (params.config.fuzzy_skin != FuzzySkinType::External) {
steep_overhang_hole = true;
}
}
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(params.config, loop.is_contour, bbox, extrusion_width, Polygons{polygon},
params.lower_slices, steep_overhang_contour, steep_overhang_hole);
}
Polygons lower_slices_polygons_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons_cache, bbox);
// get non-overhang paths by intersecting this loop with the grown lower slices
extrusion_paths_append(
@@ -381,10 +455,13 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator
} else {
const PerimeterGeneratorLoop &loop = loops[idx.first];
assert(thin_walls.empty());
ExtrusionEntityCollection children = traverse_loops_classic(params, lower_slices_polygons_cache, loop.children, thin_walls);
//w38
ExtrusionEntityCollection children = traverse_loops_classic(params, lower_slices_polygons_cache, loop.children, thin_walls,steep_overhang_contour, steep_overhang_hole);
out.entities.reserve(out.entities.size() + children.entities.size() + 1);
ExtrusionLoop *eloop = static_cast<ExtrusionLoop*>(coll.entities[idx.first]);
coll.entities[idx.first] = nullptr;
//w38
//eloop->make_counter_clockwise();
if (loop.is_contour) {
if (eloop->is_clockwise())
eloop->reverse_loop();
@@ -505,8 +582,16 @@ struct PerimeterGeneratorArachneExtrusion
bool fuzzify = false;
};
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::Parameters &params, const Polygons &lower_slices_polygons_cache, std::vector<PerimeterGeneratorArachneExtrusion> &pg_extrusions)
//w38
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::Parameters & params,
const Polygons & lower_slices_polygons_cache,
std::vector<PerimeterGeneratorArachneExtrusion> &pg_extrusions,
bool & steep_overhang_contour,
bool & steep_overhang_hole)
{
//w38
bool overhangs_reverse = params.config.overhang_reverse &&
params.layer_id % 2 == 1;
ExtrusionEntityCollection extrusion_coll;
for (PerimeterGeneratorArachneExtrusion &pg_extrusion : pg_extrusions) {
Arachne::ExtrusionLine *extrusion = pg_extrusion.extrusion;
@@ -556,6 +641,40 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role_normal,
is_external ? params.ext_perimeter_flow : params.perimeter_flow);
//w38
if (overhangs_reverse && params.config.fuzzy_skin != FuzzySkinType::None) {
if (pg_extrusion.is_contour) {
steep_overhang_contour = true;
} else if (params.config.fuzzy_skin != FuzzySkinType::External) {
steep_overhang_hole = true;
}
}
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 : 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(params.config, pg_extrusion.is_contour, extrusion_bboxs, it.first, be_clipped,
params.lower_slices, steep_overhang_contour, steep_overhang_hole)) {
break;
}
}
}
// get overhang paths by checking what parts of this loop fall
// outside the grown lower slices (thus where the distance between
// the loop centerline and original lower slices is >= half nozzle diameter
@@ -571,6 +690,9 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P
// Especially for open extrusion, we need to select a starting point that is at the start
// or the end of the extrusions to make one continuous line. Also, we prefer a non-overhang
// starting point.
//w38
ExtrusionLoop extrusion_loop(std::move(paths), pg_extrusion.is_contour ? elrDefault : elrHole);
extrusion_loop.make_counter_clockwise();
struct PointInfo
{
size_t occurrence = 0;
@@ -1105,6 +1227,23 @@ void PerimeterGenerator::add_infill_contour_for_arachne(ExPolygons infill_contou
}
//w38
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);
bool need_reverse = ((eloop->loop_role() & elrHole) == elrHole) ? steep_overhang_hole : steep_overhang_contour;
if (need_reverse) {
eloop->make_clockwise();
}
}
}
}
}
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
void PerimeterGenerator::process_arachne(
@@ -1308,8 +1447,15 @@ void PerimeterGenerator::process_arachne(
}
}
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty())
//w38
bool steep_overhang_contour = false;
bool steep_overhang_hole = false;
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions,
steep_overhang_contour, steep_overhang_hole);
!extrusion_coll.empty()) {
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole);
out_loops.append(extrusion_coll);
}
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
//w17
@@ -1645,8 +1791,15 @@ void PerimeterGenerator::process_with_one_wall_arachne(
}
}
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty())
//w38
bool steep_overhang_contour = false;
bool steep_overhang_hole = false;
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions,
steep_overhang_contour, steep_overhang_hole);
!extrusion_coll.empty()) {
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole);
out_loops.append(extrusion_coll);
}
//w16
@@ -2037,7 +2190,11 @@ void PerimeterGenerator::process_classic(
}
}
// at this point, all loops should be in contours[0]
ExtrusionEntityCollection entities = traverse_loops_classic(params, lower_layer_polygons_cache, contours.front(), thin_walls);
//w38
bool steep_overhang_contour = false;
bool steep_overhang_hole = false;
ExtrusionEntityCollection entities = traverse_loops_classic(params, lower_layer_polygons_cache, 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
// we continue inwards after having finished the brim
// TODO: add test for perimeter order

View File

@@ -39,7 +39,9 @@ struct Parameters {
scaled_resolution(scaled<double>(print_config.gcode_resolution.value)),
mm3_per_mm(perimeter_flow.mm3_per_mm()),
ext_mm3_per_mm(ext_perimeter_flow.mm3_per_mm()),
mm3_per_mm_overhang(overhang_flow.mm3_per_mm())
mm3_per_mm_overhang(overhang_flow.mm3_per_mm()),
//w38
lower_slices(lower_slices)
{
}
@@ -53,6 +55,8 @@ struct Parameters {
const PrintRegionConfig &config;
const PrintObjectConfig &object_config;
const PrintConfig &print_config;
//w38
const ExPolygons * lower_slices;
// Derived parameters
bool spiral_vase;

View File

@@ -498,7 +498,8 @@ static std::vector<std::string> s_Preset_print_options {
//w37
,"seam_slope_type", "seam_slope_conditional", "scarf_angle_threshold", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length",
"seam_slope_steps", "seam_slope_inner_walls"
};
//w38
,"overhang_reverse","overhang_reverse_threshold"};
static std::vector<std::string> s_Preset_filament_options {
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",

View File

@@ -2320,6 +2320,28 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
//w38
def = this->add("overhang_reverse", coBool);
def->label = L("Reverse on odd");
def->category = L("Layers and Perimeters");
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));
//w38
def = this->add("overhang_reverse_threshold", coFloatOrPercent);
def->label = L("Reverse threshold");
def->category = L("Layers and Perimeters");
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("parking_pos_retraction", coFloat);
def->label = L("Filament parking position");
def->tooltip = L("Distance of the extruder tip from the position where the filament is parked "

View File

@@ -693,6 +693,10 @@ PRINT_CONFIG_CLASS_DEFINE(
//w30
((ConfigOptionFloat, top_solid_infill_flow_ratio))
((ConfigOptionFloat, bottom_solid_infill_flow_ratio))
//w38
((ConfigOptionBool, overhang_reverse))
((ConfigOptionFloatOrPercent, overhang_reverse_threshold))
)
PRINT_CONFIG_CLASS_DEFINE(

View File

@@ -391,6 +391,13 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("seam_slope_steps", has_seam_slope);
toggle_field("seam_slope_inner_walls", has_seam_slope);
toggle_field("seam_slope_min_length", !config->opt_bool("seam_slope_entire_loop"));
//w38
bool has_detect_overhang_wall = config->opt_bool("overhangs");
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_field("overhang_reverse_threshold", allow_overhang_reverse && has_overhang_reverse);
}
void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config)

View File

@@ -1460,6 +1460,10 @@ void TabPrint::build()
optgroup->append_single_option_line("thick_bridges", category_path + "thick_bridges");
optgroup->append_single_option_line("overhangs", category_path + "detect-bridging-perimeters");
//w38
optgroup->append_single_option_line("overhang_reverse");
optgroup->append_single_option_line("overhang_reverse_threshold");
optgroup = page->new_optgroup(L("Advanced"));
optgroup->append_single_option_line("seam_position", category_path + "seam-position");
//Y21