mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-07 15:07:31 -06:00
Add configurable short wall/perimeter cleaning for Arachne (#2790)
* add feature to add configurable value to clean/remove short un-closed walls/perimeters, to improve print times and reduce stringing (and generally lead to a cleaner print). * fixes to short wall removal, which no longer affects bottom or top surfaces. allowed adjusting Top-surface threshold (renamed from One wall threshold for clarity) when short wall removal value is configured above default of 0.5. * small fix for toggle_line for min_width_top_surface, to only be visible if min_length_factor > 0.5 and arachne is enabled. * Use copy of input_params * revert `One wall threshold"` string change --------- Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
9701ab18e8
commit
209e9a2bb9
9 changed files with 79 additions and 28 deletions
|
@ -31,6 +31,11 @@ WallToolPathsParams make_paths_params(const int layer_id, const PrintObjectConfi
|
||||||
if (const auto &min_feature_size_opt = print_object_config.min_feature_size)
|
if (const auto &min_feature_size_opt = print_object_config.min_feature_size)
|
||||||
input_params.min_feature_size = min_feature_size_opt.value * 0.01 * min_nozzle_diameter;
|
input_params.min_feature_size = min_feature_size_opt.value * 0.01 * min_nozzle_diameter;
|
||||||
|
|
||||||
|
if (const auto &min_wall_length_factor_opt = print_object_config.min_length_factor)
|
||||||
|
input_params.min_length_factor = min_wall_length_factor_opt.value;
|
||||||
|
else
|
||||||
|
input_params.min_length_factor = 0.5f;
|
||||||
|
|
||||||
if (layer_id == 0) {
|
if (layer_id == 0) {
|
||||||
if (const auto &initial_layer_min_bead_width_opt = print_object_config.initial_layer_min_bead_width)
|
if (const auto &initial_layer_min_bead_width_opt = print_object_config.initial_layer_min_bead_width)
|
||||||
input_params.min_bead_width = initial_layer_min_bead_width_opt.value * 0.01 * min_nozzle_diameter;
|
input_params.min_bead_width = initial_layer_min_bead_width_opt.value * 0.01 * min_nozzle_diameter;
|
||||||
|
@ -47,6 +52,8 @@ WallToolPathsParams make_paths_params(const int layer_id, const PrintObjectConfi
|
||||||
|
|
||||||
input_params.wall_transition_angle = print_object_config.wall_transition_angle.value;
|
input_params.wall_transition_angle = print_object_config.wall_transition_angle.value;
|
||||||
input_params.wall_distribution_count = print_object_config.wall_distribution_count.value;
|
input_params.wall_distribution_count = print_object_config.wall_distribution_count.value;
|
||||||
|
|
||||||
|
input_params.is_top_or_bottom_layer = false; // Set to default value
|
||||||
}
|
}
|
||||||
|
|
||||||
return input_params;
|
return input_params;
|
||||||
|
@ -671,7 +678,8 @@ void WallToolPaths::removeSmallLines(std::vector<VariableWidthLines> &toolpaths)
|
||||||
coord_t min_width = std::numeric_limits<coord_t>::max();
|
coord_t min_width = std::numeric_limits<coord_t>::max();
|
||||||
for (const ExtrusionJunction &j : line)
|
for (const ExtrusionJunction &j : line)
|
||||||
min_width = std::min(min_width, j.w);
|
min_width = std::min(min_width, j.w);
|
||||||
if (line.is_odd && !line.is_closed && shorterThan(line, min_width / 2)) { // remove line
|
// Only use min_length_factor for non-topmost, to prevent top gaps. Otherwise use default value.
|
||||||
|
if (line.is_odd && !line.is_closed && shorterThan(line, m_params.is_top_or_bottom_layer ? (min_width / 2) : (min_width * m_params.min_length_factor))) { // remove line
|
||||||
line = std::move(inset.back());
|
line = std::move(inset.back());
|
||||||
inset.erase(--inset.end());
|
inset.erase(--inset.end());
|
||||||
line_idx--; // reconsider the current position
|
line_idx--; // reconsider the current position
|
||||||
|
|
|
@ -25,10 +25,12 @@ class WallToolPathsParams
|
||||||
public:
|
public:
|
||||||
float min_bead_width;
|
float min_bead_width;
|
||||||
float min_feature_size;
|
float min_feature_size;
|
||||||
|
float min_length_factor;
|
||||||
float wall_transition_length;
|
float wall_transition_length;
|
||||||
float wall_transition_angle;
|
float wall_transition_angle;
|
||||||
float wall_transition_filter_deviation;
|
float wall_transition_filter_deviation;
|
||||||
int wall_distribution_count;
|
int wall_distribution_count;
|
||||||
|
bool is_top_or_bottom_layer;
|
||||||
};
|
};
|
||||||
|
|
||||||
WallToolPathsParams make_paths_params(const int layer_id, const PrintObjectConfig &print_object_config, const PrintConfig &print_config);
|
WallToolPathsParams make_paths_params(const int layer_id, const PrintObjectConfig &print_object_config, const PrintConfig &print_config);
|
||||||
|
@ -109,7 +111,7 @@ protected:
|
||||||
/*!
|
/*!
|
||||||
* Remove polylines shorter than half the smallest line width along that polyline.
|
* Remove polylines shorter than half the smallest line width along that polyline.
|
||||||
*/
|
*/
|
||||||
static void removeSmallLines(std::vector<VariableWidthLines> &toolpaths);
|
void removeSmallLines(std::vector<VariableWidthLines> &toolpaths);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Simplifies the variable-width toolpaths by calling the simplify on every line in the toolpath using the provided
|
* Simplifies the variable-width toolpaths by calling the simplify on every line in the toolpath using the provided
|
||||||
|
|
|
@ -1941,10 +1941,15 @@ void PerimeterGenerator::process_arachne()
|
||||||
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
|
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
|
||||||
if (this->config->alternate_extra_wall && this->layer_id % 2 == 1 && !m_spiral_vase) // add alternating extra wall
|
if (this->config->alternate_extra_wall && this->layer_id % 2 == 1 && !m_spiral_vase) // add alternating extra wall
|
||||||
loop_number++;
|
loop_number++;
|
||||||
if (this->layer_id == 0 && this->config->only_one_wall_first_layer)
|
|
||||||
|
// Set the bottommost layer to be one wall
|
||||||
|
const bool is_bottom_layer = (this->layer_id == 0) ? true : false;
|
||||||
|
if (is_bottom_layer && this->config->only_one_wall_first_layer)
|
||||||
loop_number = 0;
|
loop_number = 0;
|
||||||
|
|
||||||
// Orca: set the topmost layer to be one wall according to the config
|
// Orca: set the topmost layer to be one wall according to the config
|
||||||
if (loop_number > 0 && config->only_one_wall_top && this->upper_slices == nullptr)
|
const bool is_topmost_layer = (this->upper_slices == nullptr) ? true : false;
|
||||||
|
if (is_topmost_layer && loop_number > 0 && config->only_one_wall_top)
|
||||||
loop_number = 0;
|
loop_number = 0;
|
||||||
// Orca: properly adjust offset for the outer wall if precise_outer_wall is enabled.
|
// Orca: properly adjust offset for the outer wall if precise_outer_wall is enabled.
|
||||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution),
|
ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution),
|
||||||
|
@ -1952,6 +1957,9 @@ void PerimeterGenerator::process_arachne()
|
||||||
: -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
: -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||||
|
|
||||||
Arachne::WallToolPathsParams input_params = Arachne::make_paths_params(this->layer_id, *object_config, *print_config);
|
Arachne::WallToolPathsParams input_params = Arachne::make_paths_params(this->layer_id, *object_config, *print_config);
|
||||||
|
// Set params is_top_or_bottom_layer for adjusting short-wall removal sensitivity.
|
||||||
|
input_params.is_top_or_bottom_layer = (is_bottom_layer || is_topmost_layer) ? true : false;
|
||||||
|
|
||||||
coord_t wall_0_inset = 0;
|
coord_t wall_0_inset = 0;
|
||||||
if (config->precise_outer_wall)
|
if (config->precise_outer_wall)
|
||||||
wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2);
|
wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2);
|
||||||
|
@ -1959,29 +1967,45 @@ void PerimeterGenerator::process_arachne()
|
||||||
std::vector<Arachne::VariableWidthLines> out_shell;
|
std::vector<Arachne::VariableWidthLines> out_shell;
|
||||||
ExPolygons top_fills;
|
ExPolygons top_fills;
|
||||||
ExPolygons fill_clip;
|
ExPolygons fill_clip;
|
||||||
if (loop_number > 0 && config->only_one_wall_top && !surface.is_bridge() && this->upper_slices != nullptr) {
|
|
||||||
// Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces)
|
|
||||||
ExPolygons non_top_polygons;
|
|
||||||
this->split_top_surfaces(last, top_fills, non_top_polygons, fill_clip);
|
|
||||||
|
|
||||||
if (top_fills.empty()) {
|
// Check if we're on a top surface, and make adjustments where needed
|
||||||
|
if (!surface.is_bridge() && !is_topmost_layer) {
|
||||||
|
ExPolygons non_top_polygons;
|
||||||
|
// Temporary storage, in the event all we need to do is set is_top_or_bottom_layer
|
||||||
|
ExPolygons top_fills_tmp;
|
||||||
|
ExPolygons fill_clip_tmp;
|
||||||
|
// Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces)
|
||||||
|
this->split_top_surfaces(last, top_fills_tmp, non_top_polygons, fill_clip_tmp);
|
||||||
|
|
||||||
|
if (top_fills_tmp.empty()) {
|
||||||
// No top surfaces, no special handling needed
|
// No top surfaces, no special handling needed
|
||||||
} else {
|
} else {
|
||||||
// First we slice the outer shell
|
// Use single-wall on top-surfaces if configured
|
||||||
Polygons last_p = to_polygons(last);
|
if (loop_number > 0 && config->only_one_wall_top) {
|
||||||
Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(1),
|
// Adjust arachne input params to prevent removal of larger short walls, which could lead to gaps
|
||||||
wall_0_inset, layer_height, input_params);
|
Arachne::WallToolPathsParams input_params_tmp = input_params;
|
||||||
out_shell = wallToolPaths.getToolPaths();
|
input_params_tmp.is_top_or_bottom_layer = true;
|
||||||
// Make sure infill not overlap with wall
|
|
||||||
top_fills = intersection_ex(top_fills, wallToolPaths.getInnerContour());
|
// Swap in the temporary storage
|
||||||
|
top_fills.swap(top_fills_tmp);
|
||||||
|
fill_clip.swap(fill_clip_tmp);
|
||||||
|
|
||||||
|
// First we slice the outer shell
|
||||||
|
Polygons last_p = to_polygons(last);
|
||||||
|
Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(1),
|
||||||
|
wall_0_inset, layer_height, input_params_tmp);
|
||||||
|
out_shell = wallToolPaths.getToolPaths();
|
||||||
|
// Make sure infill not overlap with wall
|
||||||
|
top_fills = intersection_ex(top_fills, wallToolPaths.getInnerContour());
|
||||||
|
|
||||||
if (!top_fills.empty()) {
|
if (!top_fills.empty()) {
|
||||||
// Then get the inner part that needs more walls
|
// Then get the inner part that needs more walls
|
||||||
last = intersection_ex(non_top_polygons, wallToolPaths.getInnerContour());
|
last = intersection_ex(non_top_polygons, wallToolPaths.getInnerContour());
|
||||||
loop_number--;
|
loop_number--;
|
||||||
} else {
|
} else {
|
||||||
// Give up the outer shell because we don't have any meaningful top surface
|
// Give up the outer shell because we don't have any meaningful top surface
|
||||||
out_shell.clear();
|
out_shell.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -804,7 +804,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||||
"initial_layer_infill_speed", "only_one_wall_top",
|
"initial_layer_infill_speed", "only_one_wall_top",
|
||||||
"timelapse_type",
|
"timelapse_type",
|
||||||
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
||||||
"wall_distribution_count", "min_feature_size", "min_bead_width", "post_process",
|
"wall_distribution_count", "min_feature_size", "min_bead_width", "post_process", "min_length_factor",
|
||||||
"small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface",
|
"small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface",
|
||||||
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk",
|
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk",
|
||||||
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap",
|
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap",
|
||||||
|
|
|
@ -4540,6 +4540,20 @@ def = this->add("filament_loading_speed", coFloats);
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->set_default_value(new ConfigOptionPercent(25));
|
def->set_default_value(new ConfigOptionPercent(25));
|
||||||
|
|
||||||
|
def = this->add("min_length_factor", coFloat);
|
||||||
|
def->label = L("Minimum wall length");
|
||||||
|
def->category = L("Quality");
|
||||||
|
def->tooltip = L("Adjust this value to prevent short, unclosed walls from being printed, which could increase print time. "
|
||||||
|
"Higher values remove more and longer walls.\n\n"
|
||||||
|
"NOTE: Bottom and top surfaces will not be affected by this value to prevent visual gaps on the ouside of the model. "
|
||||||
|
"Adjust 'One wall threshold' in the Advanced settings below to adjust the sensitivity of what is considered a top-surface. "
|
||||||
|
"'One wall threshold' is only visibile if this setting is set above the default value of 0.5, or if single-wall top surfaces is enabled.");
|
||||||
|
def->sidetext = L("");
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->min = 0.0;
|
||||||
|
def->max = 25.0;
|
||||||
|
def->set_default_value(new ConfigOptionFloat(0.5));
|
||||||
|
|
||||||
def = this->add("initial_layer_min_bead_width", coPercent);
|
def = this->add("initial_layer_min_bead_width", coPercent);
|
||||||
def->label = L("First layer minimum wall width");
|
def->label = L("First layer minimum wall width");
|
||||||
def->category = L("Quality");
|
def->category = L("Quality");
|
||||||
|
|
|
@ -784,6 +784,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||||
((ConfigOptionPercent, tree_support_top_rate))
|
((ConfigOptionPercent, tree_support_top_rate))
|
||||||
((ConfigOptionFloat, tree_support_branch_diameter_organic))
|
((ConfigOptionFloat, tree_support_branch_diameter_organic))
|
||||||
((ConfigOptionFloat, tree_support_branch_angle_organic))
|
((ConfigOptionFloat, tree_support_branch_angle_organic))
|
||||||
|
((ConfigOptionFloat, min_length_factor))
|
||||||
|
|
||||||
// Move all acceleration and jerk settings to object
|
// Move all acceleration and jerk settings to object
|
||||||
((ConfigOptionFloat, default_acceleration))
|
((ConfigOptionFloat, default_acceleration))
|
||||||
|
|
|
@ -1127,6 +1127,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||||
|| opt_key == "wall_transition_angle"
|
|| opt_key == "wall_transition_angle"
|
||||||
|| opt_key == "wall_distribution_count"
|
|| opt_key == "wall_distribution_count"
|
||||||
|| opt_key == "min_feature_size"
|
|| opt_key == "min_feature_size"
|
||||||
|
|| opt_key == "min_length_factor"
|
||||||
|| opt_key == "min_bead_width") {
|
|| opt_key == "min_bead_width") {
|
||||||
steps.emplace_back(posSlice);
|
steps.emplace_back(posSlice);
|
||||||
} else if (
|
} else if (
|
||||||
|
|
|
@ -695,7 +695,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||||
|
|
||||||
bool have_arachne = config->opt_enum<PerimeterGeneratorType>("wall_generator") == PerimeterGeneratorType::Arachne;
|
bool have_arachne = config->opt_enum<PerimeterGeneratorType>("wall_generator") == PerimeterGeneratorType::Arachne;
|
||||||
for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
||||||
"min_feature_size", "min_bead_width", "wall_distribution_count", "initial_layer_min_bead_width"})
|
"min_feature_size", "min_length_factor", "min_bead_width", "wall_distribution_count", "initial_layer_min_bead_width"})
|
||||||
toggle_line(el, have_arachne);
|
toggle_line(el, have_arachne);
|
||||||
toggle_field("detect_thin_wall", !have_arachne);
|
toggle_field("detect_thin_wall", !have_arachne);
|
||||||
|
|
||||||
|
@ -712,8 +712,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||||
toggle_line("make_overhang_printable_angle", have_make_overhang_printable);
|
toggle_line("make_overhang_printable_angle", have_make_overhang_printable);
|
||||||
toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable);
|
toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable);
|
||||||
|
|
||||||
toggle_line("min_width_top_surface",config->opt_bool("only_one_wall_top"));
|
toggle_line("min_width_top_surface", config->opt_bool("only_one_wall_top") || ((config->opt_float("min_length_factor") > 0.5f) && have_arachne)); // 0.5 is default value
|
||||||
|
|
||||||
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"));
|
||||||
|
|
||||||
|
|
|
@ -1957,6 +1957,7 @@ void TabPrint::build()
|
||||||
optgroup->append_single_option_line("initial_layer_min_bead_width");
|
optgroup->append_single_option_line("initial_layer_min_bead_width");
|
||||||
optgroup->append_single_option_line("min_bead_width");
|
optgroup->append_single_option_line("min_bead_width");
|
||||||
optgroup->append_single_option_line("min_feature_size");
|
optgroup->append_single_option_line("min_feature_size");
|
||||||
|
optgroup->append_single_option_line("min_length_factor");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Walls and surfaces"), L"param_advanced");
|
optgroup = page->new_optgroup(L("Walls and surfaces"), L"param_advanced");
|
||||||
optgroup->append_single_option_line("wall_sequence");
|
optgroup->append_single_option_line("wall_sequence");
|
||||||
|
@ -1964,8 +1965,8 @@ void TabPrint::build()
|
||||||
optgroup->append_single_option_line("print_flow_ratio");
|
optgroup->append_single_option_line("print_flow_ratio");
|
||||||
optgroup->append_single_option_line("top_solid_infill_flow_ratio");
|
optgroup->append_single_option_line("top_solid_infill_flow_ratio");
|
||||||
optgroup->append_single_option_line("bottom_solid_infill_flow_ratio");
|
optgroup->append_single_option_line("bottom_solid_infill_flow_ratio");
|
||||||
optgroup->append_single_option_line("only_one_wall_top");
|
|
||||||
optgroup->append_single_option_line("min_width_top_surface");
|
optgroup->append_single_option_line("min_width_top_surface");
|
||||||
|
optgroup->append_single_option_line("only_one_wall_top");
|
||||||
optgroup->append_single_option_line("only_one_wall_first_layer");
|
optgroup->append_single_option_line("only_one_wall_first_layer");
|
||||||
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");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue