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:
Noisyfox 2023-10-21 04:23:05 -05:00 committed by GitHub
parent bbfb9d77fa
commit 951252c597
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 199 additions and 35 deletions

View file

@ -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,
}; };

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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",

View file

@ -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");

View file

@ -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(

View file

@ -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"

View file

@ -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*/)

View file

@ -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));

View file

@ -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");

View file

@ -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));