Add fixed Ironing Angle setting for uniform surface finish (#11195)

* Initial working fixed ironing angle implemented with new Fixed ironing angle setting

* update documentation

* Combine Fill.is_using_template_angle and Fill.alternate_fill_direction into Fill.fixed_angle

* Rename SurfaceFillParams.is_using_template_angle to SurfaceFillParam.fixed_angle.
This commit is contained in:
Anson Liu
2025-11-03 01:21:01 -08:00
committed by GitHub
parent c105d42d09
commit c6e4ac1c4d
8 changed files with 43 additions and 24 deletions

View File

@@ -52,7 +52,17 @@ If this value is set to 0, the ironing toolpath will start directly at the perim
## Angle Offset ## Angle Offset
The angle of ironing lines offset relative to the top surface solid infill direction. Commonly used ironing angle offsets are 0°, 45°, and 90° each producing a [different surface finish](https://github.com/SoftFever/OrcaSlicer/issues/10834#issuecomment-3322628589) which will depend on your printer nozzle. The angle of ironing lines offset relative to the top surface solid infill direction.
Commonly used ironing angle offsets are 0°, 45°, and 90° each producing a [different surface finish](https://github.com/SoftFever/OrcaSlicer/issues/10834#issuecomment-3322628589) which will depend on your printer nozzle.
## Fixed Angle
Use a fixed absolute angle for ironing that is not offset from the top surface infill direction. This results in an ironing finish that does not have alternating line directions and may result in a more uniform surface finish and reduced tiger striping effect when reflecting light.
Set the Ironing Angle Offset to an angle with optimal ironing angle offsets from all affected top surface solid infill directions.
Suggested fixed ironing angles are 0° and 90° if you are using the default solid infill direction of 45°.
## Speed ## Speed

View File

@@ -227,8 +227,8 @@ struct SurfaceFillParams
coordf_t overlap = 0.; coordf_t overlap = 0.;
// Angle as provided by the region config, in radians. // Angle as provided by the region config, in radians.
float angle = 0.f; float angle = 0.f;
// Orca: is_using_template_angle // Orca: fixed_angle
bool is_using_template_angle = false; bool fixed_angle = false;
// Is bridging used for this fill? Bridging parameters may be used even if this->flow.bridge() is not set. // Is bridging used for this fill? Bridging parameters may be used even if this->flow.bridge() is not set.
bool bridge; bool bridge;
// Non-negative for a bridge. // Non-negative for a bridge.
@@ -284,7 +284,7 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(spacing); RETURN_COMPARE_NON_EQUAL(spacing);
RETURN_COMPARE_NON_EQUAL(overlap); RETURN_COMPARE_NON_EQUAL(overlap);
RETURN_COMPARE_NON_EQUAL(angle); RETURN_COMPARE_NON_EQUAL(angle);
RETURN_COMPARE_NON_EQUAL(is_using_template_angle); RETURN_COMPARE_NON_EQUAL(fixed_angle);
RETURN_COMPARE_NON_EQUAL(density); RETURN_COMPARE_NON_EQUAL(density);
RETURN_COMPARE_NON_EQUAL(multiline); RETURN_COMPARE_NON_EQUAL(multiline);
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust); // RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
@@ -313,7 +313,7 @@ struct SurfaceFillParams
this->spacing == rhs.spacing && this->spacing == rhs.spacing &&
this->overlap == rhs.overlap && this->overlap == rhs.overlap &&
this->angle == rhs.angle && this->angle == rhs.angle &&
this->is_using_template_angle == rhs.is_using_template_angle && this->fixed_angle == rhs.fixed_angle &&
this->bridge == rhs.bridge && this->bridge == rhs.bridge &&
this->bridge_angle == rhs.bridge_angle && this->bridge_angle == rhs.bridge_angle &&
this->density == rhs.density && this->density == rhs.density &&
@@ -902,11 +902,11 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
if (params.extrusion_role == erInternalInfill) { if (params.extrusion_role == erInternalInfill) {
params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.infill_direction.value, params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.infill_direction.value,
region_config.sparse_infill_rotate_template.value); region_config.sparse_infill_rotate_template.value);
params.is_using_template_angle = !region_config.sparse_infill_rotate_template.value.empty(); params.fixed_angle = !region_config.sparse_infill_rotate_template.value.empty();
} else { } else {
params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.solid_infill_direction.value, params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.solid_infill_direction.value,
region_config.solid_infill_rotate_template.value); region_config.solid_infill_rotate_template.value);
params.is_using_template_angle = !region_config.solid_infill_rotate_template.value.empty(); params.fixed_angle = !region_config.solid_infill_rotate_template.value.empty();
} }
params.bridge_angle = float(surface.bridge_angle); params.bridge_angle = float(surface.bridge_angle);
@@ -1094,7 +1094,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
const PrintRegionConfig &region_config = layerm.region().config(); const PrintRegionConfig &region_config = layerm.region().config();
params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.solid_infill_direction.value, params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.solid_infill_direction.value,
region_config.solid_infill_rotate_template.value); region_config.solid_infill_rotate_template.value);
params.is_using_template_angle = !region_config.solid_infill_rotate_template.value.empty(); params.fixed_angle = !region_config.solid_infill_rotate_template.value.empty();
// calculate the actual flow we'll be using for this infill // calculate the actual flow we'll be using for this infill
params.flow = layerm.flow(frSolidInfill); params.flow = layerm.flow(frSolidInfill);
@@ -1199,7 +1199,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
f->layer_id = this->id(); f->layer_id = this->id();
f->z = this->print_z; f->z = this->print_z;
f->angle = surface_fill.params.angle; f->angle = surface_fill.params.angle;
f->is_using_template_angle = surface_fill.params.is_using_template_angle; f->fixed_angle = surface_fill.params.fixed_angle;
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
f->print_config = &this->object()->print()->config(); f->print_config = &this->object()->print()->config();
f->print_object_config = &this->object()->config(); f->print_object_config = &this->object()->config();
@@ -1389,7 +1389,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
f->layer_id = this->id() - this->object()->get_layer(0)->id(); // We need to subtract raft layers. f->layer_id = this->id() - this->object()->get_layer(0)->id(); // We need to subtract raft layers.
f->z = this->print_z; f->z = this->print_z;
f->angle = surface_fill.params.angle; f->angle = surface_fill.params.angle;
f->is_using_template_angle = surface_fill.params.is_using_template_angle; f->fixed_angle = surface_fill.params.fixed_angle;
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
f->print_config = &this->object()->print()->config(); f->print_config = &this->object()->print()->config();
f->print_object_config = &this->object()->config(); f->print_object_config = &this->object()->config();
@@ -1462,7 +1462,7 @@ void Layer::make_ironing()
double height; double height;
double speed; double speed;
double angle; double angle;
bool is_using_template_angle; bool fixed_angle;
double inset; double inset;
bool operator<(const IroningParams &rhs) const { bool operator<(const IroningParams &rhs) const {
@@ -1472,7 +1472,7 @@ void Layer::make_ironing()
RETURN_COMPARE_NON_EQUAL(height); RETURN_COMPARE_NON_EQUAL(height);
RETURN_COMPARE_NON_EQUAL(speed); RETURN_COMPARE_NON_EQUAL(speed);
RETURN_COMPARE_NON_EQUAL(angle); RETURN_COMPARE_NON_EQUAL(angle);
RETURN_COMPARE_NON_EQUAL(is_using_template_angle); RETURN_COMPARE_NON_EQUAL(fixed_angle);
RETURN_COMPARE_NON_EQUAL(inset); RETURN_COMPARE_NON_EQUAL(inset);
return false; return false;
} }
@@ -1484,7 +1484,7 @@ void Layer::make_ironing()
this->height == rhs.height && this->height == rhs.height &&
this->speed == rhs.speed && this->speed == rhs.speed &&
this->angle == rhs.angle && this->angle == rhs.angle &&
this->is_using_template_angle == rhs.is_using_template_angle && this->fixed_angle == rhs.fixed_angle &&
this->pattern == rhs.pattern && this->pattern == rhs.pattern &&
this->inset == rhs.inset; this->inset == rhs.inset;
} }
@@ -1533,8 +1533,8 @@ void Layer::make_ironing()
ironing_params.inset = config.ironing_inset; ironing_params.inset = config.ironing_inset;
ironing_params.height = default_layer_height * 0.01 * config.ironing_flow; ironing_params.height = default_layer_height * 0.01 * config.ironing_flow;
ironing_params.speed = config.ironing_speed; ironing_params.speed = config.ironing_speed;
ironing_params.angle = calculate_infill_rotation_angle(this->object(), this->id(), config.solid_infill_direction.value, config.solid_infill_rotate_template.value) + config.ironing_angle * M_PI / 180.; ironing_params.angle = (config.ironing_angle_fixed ? 0 : calculate_infill_rotation_angle(this->object(), this->id(), config.solid_infill_direction.value, config.solid_infill_rotate_template.value)) + config.ironing_angle * M_PI / 180.;
ironing_params.is_using_template_angle = !config.solid_infill_rotate_template.value.empty(); ironing_params.fixed_angle = config.ironing_angle_fixed || !config.solid_infill_rotate_template.value.empty();
ironing_params.pattern = config.ironing_pattern; ironing_params.pattern = config.ironing_pattern;
ironing_params.layerm = layerm; ironing_params.layerm = layerm;
by_extruder.emplace_back(ironing_params); by_extruder.emplace_back(ironing_params);
@@ -1630,7 +1630,7 @@ void Layer::make_ironing()
// Create the filler object. // Create the filler object.
f->spacing = ironing_params.line_spacing; f->spacing = ironing_params.line_spacing;
f->angle = float(ironing_params.angle); f->angle = float(ironing_params.angle);
f->is_using_template_angle = ironing_params.is_using_template_angle; f->fixed_angle = ironing_params.fixed_angle;
f->link_max_length = (coord_t) scale_(3. * f->spacing); f->link_max_length = (coord_t) scale_(3. * f->spacing);
double extrusion_height = ironing_params.height * f->spacing / nozzle_dmr; double extrusion_height = ironing_params.height * f->spacing / nozzle_dmr;
float extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(float(nozzle_dmr), float(extrusion_height)); float extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(float(nozzle_dmr), float(extrusion_height));

View File

@@ -304,10 +304,9 @@ std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const
printf("Filling bridge with angle %f\n", surface->bridge_angle); printf("Filling bridge with angle %f\n", surface->bridge_angle);
#endif /* SLIC3R_DEBUG */ #endif /* SLIC3R_DEBUG */
out_angle = float(surface->bridge_angle); out_angle = float(surface->bridge_angle);
} else if (this->layer_id != size_t(-1)) { } else if (this->layer_id != size_t(-1) && !fixed_angle) {
// alternate fill direction // alternate fill direction
//Orca: if template angle is not empty, don't apply layer angle //Orca: Do not alternate direction if Fill.fixed_angle is true
if(!is_using_template_angle)
out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers); out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers);
} else { } else {
// printf("Layer_ID undefined!\n"); // printf("Layer_ID undefined!\n");

View File

@@ -119,8 +119,9 @@ public:
coordf_t overlap; coordf_t overlap;
// in radians, ccw, 0 = East // in radians, ccw, 0 = East
float angle; float angle;
// Orca: is_using_template_angle
bool is_using_template_angle{false}; // Orca: Fill direction is fixed absolute angle if SurfaceFillParams.fixed_angle or config.ironing_angle_fixed
bool fixed_angle{false};
// In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines. // In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines.
// Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic. // Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic.
// If left to zero, the links will not be limited. // If left to zero, the links will not be limited.
@@ -203,7 +204,7 @@ protected:
ExPolygon expolygon, ExPolygon expolygon,
ThickPolylines& thick_polylines_out) {} ThickPolylines& thick_polylines_out) {}
virtual float _layer_angle(size_t idx) const { return is_using_template_angle ? 0.f : (idx & 1) ? float(M_PI/2.) : 0.f; } virtual float _layer_angle(size_t idx) const { return fixed_angle ? 0.f : (idx & 1) ? float(M_PI/2.) : 0.f; }
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const; virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;

View File

@@ -888,7 +888,7 @@ static std::vector<std::string> s_Preset_print_options {
"infill_direction", "solid_infill_direction", "counterbore_hole_bridging","infill_shift_step", "sparse_infill_rotate_template", "solid_infill_rotate_template", "symmetric_infill_y_axis","skeleton_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_density", "infill_direction", "solid_infill_direction", "counterbore_hole_bridging","infill_shift_step", "sparse_infill_rotate_template", "solid_infill_rotate_template", "symmetric_infill_y_axis","skeleton_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_density",
"align_infill_direction_to_model", "extra_solid_infills", "align_infill_direction_to_model", "extra_solid_infills",
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target", "minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target",
"ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_inset", "ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_angle_fixed", "ironing_inset",
"support_ironing", "support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing", "support_ironing", "support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing",
"max_travel_detour_distance", "max_travel_detour_distance",
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_mode", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence", "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_mode", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence",

View File

@@ -3887,6 +3887,13 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0)); def->set_default_value(new ConfigOptionFloat(0));
def = this->add("ironing_angle_fixed", coBool);
def->label = L("Fixed ironing angle");
def->category = L("Quality");
def->tooltip = L("Use a fixed absolute angle for ironing.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("layer_change_gcode", coString); def = this->add("layer_change_gcode", coString);
def->label = L("Layer change G-code"); def->label = L("Layer change G-code");
def->tooltip = L("This G-code is inserted at every layer change after the Z lift."); def->tooltip = L("This G-code is inserted at every layer change after the Z lift.");

View File

@@ -1084,6 +1084,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, ironing_direction)) ((ConfigOptionFloat, ironing_direction))
((ConfigOptionFloat, ironing_speed)) ((ConfigOptionFloat, ironing_speed))
((ConfigOptionFloat, ironing_angle)) ((ConfigOptionFloat, ironing_angle))
((ConfigOptionBool, ironing_angle_fixed))
// Detect bridging perimeters // Detect bridging perimeters
((ConfigOptionBool, detect_overhang_wall)) ((ConfigOptionBool, detect_overhang_wall))
((ConfigOptionInt, wall_filament)) ((ConfigOptionInt, wall_filament))

View File

@@ -2339,6 +2339,7 @@ void TabPrint::build()
optgroup->append_single_option_line("ironing_spacing", "quality_settings_ironing#line-spacing"); optgroup->append_single_option_line("ironing_spacing", "quality_settings_ironing#line-spacing");
optgroup->append_single_option_line("ironing_inset", "quality_settings_ironing#inset"); optgroup->append_single_option_line("ironing_inset", "quality_settings_ironing#inset");
optgroup->append_single_option_line("ironing_angle", "quality_settings_ironing#angle-offset"); optgroup->append_single_option_line("ironing_angle", "quality_settings_ironing#angle-offset");
optgroup->append_single_option_line("ironing_angle_fixed", "quality_settings_ironing#fixed-angle");
optgroup = page->new_optgroup(L("Wall generator"), L"param_wall_generator"); optgroup = page->new_optgroup(L("Wall generator"), L"param_wall_generator");
optgroup->append_single_option_line("wall_generator", "quality_settings_wall_generator"); optgroup->append_single_option_line("wall_generator", "quality_settings_wall_generator");