Skip to content

Commit 5dc277b

Browse files
committed
feat(perimeters): Reverse extrusion on odd layers
Add options to reverse the extrusion directio for overhang perimeters, internal perimeters and infill on odd layers. These options help to improve steep overhangs and can help reduce internal stress and warping in large parts. Ported from OrcaSlicer (Noisyfox @Noisyfox)
1 parent e7ad383 commit 5dc277b

8 files changed

Lines changed: 87 additions & 5 deletions

File tree

src/libslic3r/ExtrusionRole.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
///|/ Copyright (c) 2023 Robert Schiele @schiele
22
///|/ Copyright (c) Prusa Research 2023 Vojtěch Bubník @bubnikv
3+
///|/ Copyright (c) OrcaSlicer 2023 Noisyfox @Noisyfox
34
///|/
45
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
56
///|/
@@ -86,7 +87,9 @@ struct ExtrusionRole : public ExtrusionRoleModifiers
8687
static constexpr const ExtrusionRoleModifiers Mixed{ ExtrusionRoleModifier::Mixed };
8788

8889
bool is_perimeter() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Perimeter); }
90+
bool is_internal_perimeter() const { return this->is_perimeter() && !this->is_external(); }
8991
bool is_external_perimeter() const { return this->is_perimeter() && this->is_external(); }
92+
bool is_overhang_perimeter() const { return this->is_perimeter() && this->is_bridge(); }
9093
bool is_infill() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Infill); }
9194
bool is_solid_infill() const { return this->is_infill() && this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); }
9295
bool is_sparse_infill() const { return this->is_infill() && ! this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); }

src/libslic3r/GCode/ExtrusionOrder.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,29 @@ std::vector<Perimeter> extract_perimeter_extrusions(
124124
for (ExtrusionEntity *ee : *eec) {
125125
if (ee != nullptr) {
126126
std::optional<InstancePoint> last_position{get_instance_point(previous_position, offset)};
127+
ExtrusionRole role = ee->role();
127128
bool reverse_loop{false};
128129
if (auto loop = dynamic_cast<const ExtrusionLoop *>(ee)) {
129130
const bool is_hole = loop->is_clockwise();
130-
reverse_loop = print.config().prefer_clockwise_movements ? !is_hole : is_hole;
131+
132+
const bool is_odd_layer = layer.id() % 2 == 1;
133+
const bool should_reverse_internal_perimeter = role.is_internal_perimeter() &&
134+
region.config().internal_perimeters_reverse;
135+
bool should_reverse_overhang_perimeters{false};
136+
137+
if (region.config().overhangs_reverse && is_odd_layer) {
138+
for (const ExtrusionPath &el : loop->paths) {
139+
if (el.role().is_overhang_perimeter()) {
140+
should_reverse_overhang_perimeters = true;
141+
break;
142+
}
143+
}
144+
}
145+
146+
const bool reverse_on_odd = is_odd_layer &&
147+
(should_reverse_internal_perimeter || should_reverse_overhang_perimeters);
148+
reverse_loop = (print.config().prefer_clockwise_movements ? !is_hole : is_hole) ^
149+
reverse_on_odd;
131150
}
132151
auto [path, wipe_offset]{smooth_path(&layer, &region, ExtrusionEntityReference{*ee, reverse_loop}, extruder_id, last_position)};
133152
previous_position = get_gcode_point(last_position, offset);
@@ -190,11 +209,16 @@ std::vector<InfillRange> extract_infill_ranges(
190209
const std::optional<InstancePoint> previous_instance_point{get_instance_point(previous_position, offset)};
191210
const Point* start_near{previous_instance_point ? &(previous_instance_point->local_point) : nullptr};
192211
const ExtrusionEntityReferences sorted_extrusions{sort_fill_extrusions(extrusions, start_near)};
212+
const bool reverse_infill = region.config().infill_reverse && layer.id() % 2 == 1;
193213

194214
std::vector<SmoothPath> paths;
195215
for (const ExtrusionEntityReference &extrusion_reference : sorted_extrusions) {
196216
std::optional<InstancePoint> last_position{get_instance_point(previous_position, offset)};
197-
auto [path, _]{smooth_path(&layer, &region, extrusion_reference, extruder_id, last_position)};
217+
auto [path, _]{smooth_path(
218+
&layer, &region,
219+
ExtrusionEntityReference{extrusion_reference.extrusion_entity(), reverse_infill},
220+
extruder_id, last_position
221+
)};
198222
if (!path.empty()) {
199223
paths.push_back(std::move(path));
200224
}

src/libslic3r/Preset.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
///|/ Copyright (c) 2021 Ilya @xorza
44
///|/ Copyright (c) 2019 John Drake @foxox
55
///|/ Copyright (c) 2018 Martin Loidl @LoidlM
6+
///|/ Copyright (c) OrcaSlicer 2023 Noisyfox @Noisyfox
67
///|/
78
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
89
///|/
@@ -567,6 +568,10 @@ static std::vector<std::string> s_Preset_print_options {
567568
"wipe_tower_minimum_cruise_ratio",
568569

569570
"alternate_extra_perimeter",
571+
572+
"overhangs_reverse",
573+
"infill_reverse",
574+
"internal_perimeters_reverse",
570575
};
571576

572577
static std::vector<std::string> s_Preset_filament_options {

src/libslic3r/PrintConfig.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
///|/ Copyright (c) SuperSlicer 2019 Remi Durand @supermerill
2020
///|/ Copyright (c) OrcaSlicer 2023 SoftFever @SoftFever
2121
///|/ Copyright (c) 2024 Vovodroid @Vovodroid
22+
///|/ Copyright (c) OrcaSlicer 2023 Noisyfox @Noisyfox
2223
///|/
2324
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
2425
///|/
@@ -4477,6 +4478,31 @@ void PrintConfigDef::init_fff_params()
44774478
def->min = 0;
44784479
def->set_default_value(new ConfigOptionFloatOrPercent(85, true));
44794480

4481+
def = this->add("overhangs_reverse", coBool);
4482+
def->label = L("For overhang perimeters");
4483+
def->full_label = L("Reverse on odd layers: Overhangs");
4484+
def->category = L("Layers and Perimeters");
4485+
def->tooltip = L("Extrude perimeters that have a part over an overhang in the opposite direction on odd layers "
4486+
"to improve steep overhangs.");
4487+
def->mode = comAdvanced;
4488+
def->set_default_value(new ConfigOptionBool(false));
4489+
4490+
def = this->add("infill_reverse", coBool);
4491+
def->label = L("For infill");
4492+
def->full_label = L("Reverse on odd layers: Infill");
4493+
def->category = L("Infill");
4494+
def->tooltip = L("Extrude infill in the opposite direction on odd layers to reduce stress and warping.");
4495+
def->mode = comAdvanced;
4496+
def->set_default_value(new ConfigOptionBool(false));
4497+
4498+
def = this->add("internal_perimeters_reverse", coBool);
4499+
def->label = L("For internal perimeters");
4500+
def->full_label = L("Reverse on odd layers: Internal Perimeters");
4501+
def->category = L("Layers and Perimeters");
4502+
def->tooltip = L("Extrude internal perimeters in the opposite direction on odd layers to reduce stress and warping.");
4503+
def->mode = comAdvanced;
4504+
def->set_default_value(new ConfigOptionBool(false));
4505+
44804506
// Declare retract values for filament profile, overriding the printer's extruder profile.
44814507
for (const char *opt_key : {
44824508
// floats

src/libslic3r/PrintConfig.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
///|/ Copyright (c) Slic3r 2013 - 2015 Alessandro Ranellucci @alranel
1313
///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard
1414
///|/ Copyright (c) 2015 Alexander Rössler @machinekoder
15+
///|/ Copyright (c) OrcaSlicer 2023 Noisyfox @Noisyfox
1516
///|/
1617
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
1718
///|/
@@ -817,6 +818,10 @@ PRINT_CONFIG_CLASS_DEFINE(
817818

818819
// SuperSlicer
819820
((ConfigOptionEnum<InfillPattern>, solid_fill_pattern))
821+
// Reverse extrusion direction
822+
((ConfigOptionBool, overhangs_reverse))
823+
((ConfigOptionBool, infill_reverse))
824+
((ConfigOptionBool, internal_perimeters_reverse))
820825
)
821826

822827
PRINT_CONFIG_CLASS_DEFINE(

src/libslic3r/PrintObject.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
///|/ Copyright (c) 2016 Joseph Lenox @lordofhyphens
55
///|/ Copyright (c) Slic3r 2014 - 2016 Alessandro Ranellucci @alranel
66
///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard
7+
///|/ Copyright (c) OrcaSlicer 2023 Noisyfox @Noisyfox
78
///|/
89
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
910
///|/
@@ -731,7 +732,9 @@ bool PrintObject::invalidate_state_by_config_options(
731732
|| opt_key == "arc_fitting"
732733
|| opt_key == "top_one_perimeter_type"
733734
|| opt_key == "only_one_perimeter_first_layer"
734-
|| opt_key == "alternate_extra_perimeter") {
735+
|| opt_key == "alternate_extra_perimeter"
736+
|| opt_key == "overhangs_reverse"
737+
|| opt_key == "internal_perimeters_reverse") {
735738
steps.emplace_back(posPerimeters);
736739
} else if (
737740
opt_key == "small_area_infill_flow_compensation"
@@ -840,7 +843,8 @@ bool PrintObject::invalidate_state_by_config_options(
840843
|| opt_key == "raft_first_layer_density"
841844
|| opt_key == "raft_first_layer_expansion"
842845
|| opt_key == "dont_support_bridges"
843-
|| opt_key == "first_layer_extrusion_width") {
846+
|| opt_key == "first_layer_extrusion_width"
847+
|| opt_key == "infill_reverse") {
844848
steps.emplace_back(posSupportMaterial);
845849
} else if (opt_key == "bottom_solid_layers") {
846850
steps.emplace_back(posPrepareInfill);

src/slic3r/GUI/ConfigManipulation.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Pavel Mikuš @Godrak, Tomáš Mészáros @tamasmeszaros
2+
///|/ Copyright (c) OrcaSlicer 2023 Noisyfox @Noisyfox
23
///|/
34
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
45
///|/
@@ -140,11 +141,15 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
140141
fill_density == 0 &&
141142
! config->opt_bool("support_material") &&
142143
config->opt_int("support_material_enforce_layers") == 0 &&
143-
! config->opt_bool("thin_walls")))
144+
! config->opt_bool("thin_walls") &&
145+
! config->opt_bool("overhangs_reverse") &&
146+
! config->opt_bool("infill_reverse") &&
147+
! config->opt_bool("internal_perimeters_reverse")))
144148
{
145149
wxString msg_text = _(L("The Spiral Vase mode requires:\n"
146150
"- one perimeter\n"
147151
"- no extra perimeter on odd layers\n"
152+
"- no reverse on odd layers\n"
148153
"- no top solid layers\n"
149154
"- 0% fill density\n"
150155
"- no support material\n"
@@ -163,6 +168,9 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
163168
new_conf.set_key_value("support_material", new ConfigOptionBool(false));
164169
new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0));
165170
new_conf.set_key_value("thin_walls", new ConfigOptionBool(false));
171+
new_conf.set_key_value("overhangs_reverse", new ConfigOptionBool(false));
172+
new_conf.set_key_value("infill_reverse", new ConfigOptionBool(false));
173+
new_conf.set_key_value("internal_perimeters_reverse", new ConfigOptionBool(false));
166174
fill_density = 0;
167175
support = false;
168176
}

src/slic3r/GUI/Tab.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
///|/ Copyright (c) 2016 Chow Loong Jin @hyperair
1515
///|/ Copyright (c) 2012 QuantumConcepts
1616
///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen
17+
///|/ Copyright (c) OrcaSlicer 2023 Noisyfox @Noisyfox
1718
///|/
1819
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
1920
///|/
@@ -1837,6 +1838,12 @@ void TabPrint::build()
18371838
optgroup->append_single_option_line("xy_size_compensation");
18381839
optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487");
18391840

1841+
line = { L("Reverse direction on odd layers"), "" };
1842+
line.append_option(optgroup->get_option("overhangs_reverse"));
1843+
line.append_option(optgroup->get_option("infill_reverse"));
1844+
line.append_option(optgroup->get_option("internal_perimeters_reverse"));
1845+
optgroup->append_line(line);
1846+
18401847
optgroup = page->new_optgroup(L("Arachne perimeter generator"));
18411848
optgroup->append_single_option_line("wall_transition_angle");
18421849
optgroup->append_single_option_line("wall_transition_filter_deviation");

0 commit comments

Comments
 (0)