Replaced parameters wall_add_middle_threshold and wall_split_middle_threshold with automatic computation based on extrusion width.

That allows computed better values in cases when the extrusion width of the first layer or for the infill differs from the extrusion width for perimeters.

Parameters min_feature_size and wall_transition_length now can be set in percentages, and then they will be calculated based on nozzle size. Old profiles with default values are forced to replace old default values with new default values in percentages.
This commit is contained in:
Lukáš Hejl 2022-08-05 08:16:45 +02:00
parent b8c3905e82
commit c9ddf7315e
14 changed files with 70 additions and 72 deletions

View File

@ -24,31 +24,37 @@ namespace Slic3r::Arachne
{
WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x,
const size_t inset_count, const coord_t wall_0_inset, const PrintObjectConfig &print_object_config, const PrintConfig &print_config)
const size_t inset_count, const coord_t wall_0_inset, const coordf_t layer_height,
const PrintObjectConfig &print_object_config, const PrintConfig &print_config)
: outline(outline)
, bead_width_0(bead_width_0)
, bead_width_x(bead_width_x)
, inset_count(inset_count)
, wall_0_inset(wall_0_inset)
, layer_height(layer_height)
, print_thin_walls(Slic3r::Arachne::fill_outline_gaps)
, min_feature_size(scaled<coord_t>(print_object_config.min_feature_size.value))
, min_bead_width(scaled<coord_t>(print_object_config.min_bead_width.value))
, small_area_length(static_cast<double>(bead_width_0) / 2.)
, wall_transition_filter_deviation(scaled<coord_t>(print_object_config.wall_transition_filter_deviation.value))
, wall_transition_length(scaled<coord_t>(print_object_config.wall_transition_length.value))
, toolpaths_generated(false)
, print_object_config(print_object_config)
{
if (const auto &min_bead_width_opt = print_object_config.min_bead_width; min_bead_width_opt.percent) {
assert(!print_config.nozzle_diameter.empty());
double min_nozzle_diameter = *std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end());
this->min_bead_width = scaled<coord_t>(min_bead_width_opt.value * 0.01 * min_nozzle_diameter);
}
this->min_nozzle_diameter = float(*std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end()));
if (const auto &wall_transition_filter_deviation_opt = print_object_config.wall_transition_filter_deviation; wall_transition_filter_deviation_opt.percent) {
assert(!print_config.nozzle_diameter.empty());
double min_nozzle_diameter = *std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end());
this->wall_transition_filter_deviation = scaled<coord_t>(wall_transition_filter_deviation_opt.value * 0.01 * min_nozzle_diameter);
}
if (const auto &min_feature_size_opt = print_object_config.min_feature_size; min_feature_size_opt.percent)
this->min_feature_size = scaled<coord_t>(min_feature_size_opt.value * 0.01 * this->min_nozzle_diameter);
if (const auto &min_bead_width_opt = print_object_config.min_bead_width; min_bead_width_opt.percent)
this->min_bead_width = scaled<coord_t>(min_bead_width_opt.value * 0.01 * this->min_nozzle_diameter);
if (const auto &wall_transition_filter_deviation_opt = print_object_config.wall_transition_filter_deviation; wall_transition_filter_deviation_opt.percent)
this->wall_transition_filter_deviation = scaled<coord_t>(wall_transition_filter_deviation_opt.value * 0.01 * this->min_nozzle_diameter);
if (const auto &wall_transition_length_opt = print_object_config.wall_transition_length; wall_transition_length_opt.percent)
this->wall_transition_length = scaled<coord_t>(wall_transition_length_opt.value * 0.01 * this->min_nozzle_diameter);
}
void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const int64_t allowed_error_distance_squared)
@ -490,9 +496,12 @@ const std::vector<VariableWidthLines> &WallToolPaths::generate()
return toolpaths;
}
const coord_t wall_transition_length = scaled<coord_t>(this->print_object_config.wall_transition_length.value);
const double wall_split_middle_threshold = this->print_object_config.wall_split_middle_threshold.value / 100.; // For an uneven nr. of lines: When to split the middle wall into two.
const double wall_add_middle_threshold = this->print_object_config.wall_add_middle_threshold.value / 100.; // For an even nr. of lines: When to add a new middle in between the innermost two walls.
const float external_perimeter_extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(unscale<float>(bead_width_0), float(this->layer_height));
const float perimeter_extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(unscale<float>(bead_width_x), float(this->layer_height));
const double wall_split_middle_threshold = std::clamp(2. * unscaled<double>(this->min_bead_width) / external_perimeter_extrusion_width - 1., 0.01, 0.99); // For an uneven nr. of lines: When to split the middle wall into two.
const double wall_add_middle_threshold = std::clamp(unscaled<double>(this->min_bead_width) / perimeter_extrusion_width, 0.01, 0.99); // For an even nr. of lines: When to add a new middle in between the innermost two walls.
const int wall_distribution_count = this->print_object_config.wall_distribution_count.value;
const size_t max_bead_count = (inset_count < std::numeric_limits<coord_t>::max() / 2) ? 2 * inset_count : std::numeric_limits<coord_t>::max();
const auto beading_strat = BeadingStrategyFactory::makeStrategy

View File

@ -31,7 +31,7 @@ public:
* \param inset_count The maximum number of parallel extrusion lines that make up the wall
* \param wall_0_inset How far to inset the outer wall, to make it adhere better to other walls.
*/
WallToolPaths(const Polygons& outline, coord_t bead_width_0, coord_t bead_width_x, size_t inset_count, coord_t wall_0_inset, const PrintObjectConfig &print_object_config, const PrintConfig &print_config);
WallToolPaths(const Polygons& outline, coord_t bead_width_0, coord_t bead_width_x, size_t inset_count, coord_t wall_0_inset, coordf_t layer_height, const PrintObjectConfig &print_object_config, const PrintConfig &print_config);
/*!
* Generates the Toolpaths
@ -110,11 +110,14 @@ private:
coord_t bead_width_x; //<! The subsequently extrusion line width with which libArachne generates its walls if WallToolPaths was called with the nominal_bead_width Constructor this is the same as bead_width_0
size_t inset_count; //<! The maximum number of walls to generate
coord_t wall_0_inset; //<! How far to inset the outer wall. Should only be applied when printing the actual walls, not extra infill/skin/support walls.
coordf_t layer_height;
bool print_thin_walls; //<! Whether to enable the widening beading meta-strategy for thin features
coord_t min_feature_size; //<! The minimum size of the features that can be widened by the widening beading meta-strategy. Features thinner than that will not be printed
coord_t min_bead_width; //<! The minimum bead size to use when widening thin model features with the widening beading meta-strategy
double small_area_length; //<! The length of the small features which are to be filtered out, this is squared into a surface
coord_t wall_transition_filter_deviation; //!< The allowed line width deviation induced by filtering
coord_t wall_transition_length;
float min_nozzle_diameter;
bool toolpaths_generated; //<! Are the toolpaths generated
std::vector<VariableWidthLines> toolpaths; //<! The generated toolpaths
Polygons inner_contour; //<! The inner contour of the generated toolpaths

View File

@ -388,6 +388,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.anchor_length_max = surface_fill.params.anchor_length_max;
params.resolution = resolution;
params.use_arachne = perimeter_generator == PerimeterGeneratorType::Arachne && surface_fill.params.pattern == ipConcentric;
params.layer_height = m_regions[surface_fill.region_id]->layer()->height;
for (ExPolygon &expoly : surface_fill.expolygons) {
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.

View File

@ -61,6 +61,8 @@ struct FillParams
// For Concentric infill, to switch between Classic and Arachne.
bool use_arachne { false };
// Layer height for Concentric infill with Arachne.
coordf_t layer_height { 0.f };
};
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");

View File

@ -77,8 +77,8 @@ void FillConcentric::_fill_surface_single(const FillParams &params,
if (params.density > 0.9999f && !params.dont_adjust) {
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
Polygons polygons = offset(expolygon, min_spacing / 2);
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, *this->print_object_config, *this->print_config);
Polygons polygons = offset(expolygon, float(min_spacing) / 2.f);
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, params.layer_height, *this->print_object_config, *this->print_config);
std::vector<Arachne::VariableWidthLines> loops = wallToolPaths.getToolPaths();
std::vector<const Arachne::ExtrusionLine *> all_extrusions;

View File

@ -454,6 +454,7 @@ namespace Slic3r {
bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version);
unsigned int version() const { return m_version; }
boost::optional<Semver> prusaslicer_generator_version() const { return m_prusaslicer_generator_version; }
private:
void _destroy_xml_parser();
@ -3098,8 +3099,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
}
// Perform conversions based on the config values available.
//FIXME provide a version of PrusaSlicer that stored the project file (3MF).
static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config)
static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config, const boost::optional<Semver>& prusaslicer_generator_version)
{
if (! config.has("brim_separation")) {
if (auto *opt_elephant_foot = config.option<ConfigOptionFloat>("elefant_foot_compensation", false); opt_elephant_foot) {
@ -3108,6 +3108,23 @@ static void handle_legacy_project_loaded(unsigned int version_project_file, Dyna
opt_brim_separation->value = opt_elephant_foot->value;
}
}
// In PrusaSlicer 2.5.0-alpha2 and 2.5.0-alpha3, we introduce several parameters for Arachne that depend
// on nozzle size . Later we decided to make default values for those parameters computed automatically
// until the user changes them.
if (prusaslicer_generator_version && *prusaslicer_generator_version >= *Semver::parse("2.5.0-alpha2") && *prusaslicer_generator_version <= *Semver::parse("2.5.0-alpha3")) {
if (auto *opt_wall_transition_length = config.option<ConfigOptionFloatOrPercent>("wall_transition_length", false);
opt_wall_transition_length && !opt_wall_transition_length->percent && opt_wall_transition_length->value == 0.4) {
opt_wall_transition_length->percent = true;
opt_wall_transition_length->value = 100;
}
if (auto *opt_min_feature_size = config.option<ConfigOptionFloatOrPercent>("min_feature_size", false);
opt_min_feature_size && !opt_min_feature_size->percent && opt_min_feature_size->value == 0.1) {
opt_min_feature_size->percent = true;
opt_min_feature_size->value = 25;
}
}
}
bool is_project_3mf(const std::string& filename)
@ -3150,7 +3167,7 @@ bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionCo
_3MF_Importer importer;
bool res = importer.load_model_from_file(path, *model, config, config_substitutions, check_version);
importer.log_errors();
handle_legacy_project_loaded(importer.version(), config);
handle_legacy_project_loaded(importer.version(), config, importer.prusaslicer_generator_version());
return res;
}

View File

@ -574,7 +574,7 @@ void PerimeterGenerator::process_arachne()
ExPolygons last = offset_ex(surface.expolygon.simplify_p(m_scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
Polygons last_p = to_polygons(last);
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, *this->object_config, *this->print_config);
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, layer_height, *this->object_config, *this->print_config);
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
loop_number = int(perimeters.size()) - 1;

View File

@ -448,7 +448,7 @@ static std::vector<std::string> s_Preset_print_options {
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
"perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"wall_distribution_count", "wall_split_middle_threshold", "wall_add_middle_threshold", "min_feature_size", "min_bead_width"
"wall_distribution_count", "min_feature_size", "min_bead_width"
};
static std::vector<std::string> s_Preset_filament_options {

View File

@ -3076,15 +3076,16 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Arachne));
def = this->add("wall_transition_length", coFloat);
def = this->add("wall_transition_length", coFloatOrPercent);
def->label = L("Perimeter transition length");
def->category = L("Advanced");
def->tooltip = L("When transitioning between different numbers of perimeters as the part becomes "
"thinner, a certain amount of space is allotted to split or join the perimeter segments.");
def->sidetext = L("mm");
"thinner, a certain amount of space is allotted to split or join the perimeter segments. "
"If expressed as a percentage (for example 100%), it will be computed based on the nozzle diameter.");
def->sidetext = L("mm or %");
def->mode = comExpert;
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0.4));
def->set_default_value(new ConfigOptionFloatOrPercent(100, true));
def = this->add("wall_transition_filter_deviation", coFloatOrPercent);
def->label = L("Perimeter transitioning filter margin");
@ -3123,46 +3124,17 @@ void PrintConfigDef::init_fff_params()
def->min = 1;
def->set_default_value(new ConfigOptionInt(1));
def = this->add("wall_split_middle_threshold", coPercent);
def->label = L("Split middle perimeter threshold");
def->category = L("Advanced");
def->tooltip = L("The smallest extrusion width, as a factor of the normal extrusion width, above which the middle "
"perimeter (if there is one) will be split into two. Reduce this setting to use more, thinner "
"perimeters. Increase to use fewer, wider perimeters. Note that this applies -as if- the entire "
"shape should be filled with perimeter, so the middle here refers to the middle of the object "
"between two outer edges of the shape, even if there actually is infill or other extrusion types in "
"the print instead of the perimeter.");
def->sidetext = L("%");
def->mode = comAdvanced;
def->min = 1;
def->max = 99;
def->set_default_value(new ConfigOptionPercent(50));
def = this->add("wall_add_middle_threshold", coPercent);
def->label = L("Add middle perimeter threshold");
def->category = L("Advanced");
def->tooltip = L("The smallest extrusion width, as a factor of the normal extrusion width, above which a middle "
"perimeter (if there wasn't one already) will be added. Reduce this setting to use more, "
"thinner perimeters. Increase to use fewer, wider perimeters. Note that this applies -as if- the "
"entire shape should be filled with perimeter, so the middle here refers to the middle of the "
"object between two outer edges of the shape, even if there actually is infill or other "
"extrusion types in the print instead of the perimeter.");
def->sidetext = L("%");
def->mode = comAdvanced;
def->min = 1;
def->max = 99;
def->set_default_value(new ConfigOptionPercent(75));
def = this->add("min_feature_size", coFloat);
def = this->add("min_feature_size", coFloatOrPercent);
def->label = L("Minimum feature size");
def->category = L("Advanced");
def->tooltip = L("Minimum thickness of thin features. Model features that are thinner than this value will "
"not be printed, while features thicker than the Minimum feature size will be widened to "
"the Minimum perimeter width.");
def->sidetext = L("mm");
"the Minimum perimeter width. "
"If expressed as a percentage (for example 25%), it will be computed based on the nozzle diameter.");
def->sidetext = L("mm or %");
def->mode = comExpert;
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0.1));
def->set_default_value(new ConfigOptionFloatOrPercent(25, true));
def = this->add("min_bead_width", coFloatOrPercent);
def->label = L("Minimum perimeter width");
@ -4012,6 +3984,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
"serial_port", "serial_speed",
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
// Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width.
"wall_add_middle_threshold", "wall_split_middle_threshold",
};
// In PrusaSlicer 2.3.0-alpha0 the "monotonous" infill was introduced, which was later renamed to "monotonic".

View File

@ -491,13 +491,11 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, slice_closing_radius))
((ConfigOptionEnum<SlicingMode>, slicing_mode))
((ConfigOptionEnum<PerimeterGeneratorType>, perimeter_generator))
((ConfigOptionFloat, wall_transition_length))
((ConfigOptionFloatOrPercent, wall_transition_length))
((ConfigOptionFloatOrPercent, wall_transition_filter_deviation))
((ConfigOptionFloat, wall_transition_angle))
((ConfigOptionInt, wall_distribution_count))
((ConfigOptionPercent, wall_split_middle_threshold))
((ConfigOptionPercent, wall_add_middle_threshold))
((ConfigOptionFloat, min_feature_size))
((ConfigOptionFloatOrPercent, min_feature_size))
((ConfigOptionFloatOrPercent, min_bead_width))
((ConfigOptionBool, support_material))
// Automatic supports (generated based on support_material_threshold).

View File

@ -673,8 +673,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "wall_transition_filter_deviation"
|| opt_key == "wall_transition_angle"
|| opt_key == "wall_distribution_count"
|| opt_key == "wall_split_middle_threshold"
|| opt_key == "wall_add_middle_threshold"
|| opt_key == "min_feature_size"
|| opt_key == "min_bead_width") {
steps.emplace_back(posSlice);

View File

@ -323,8 +323,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("wall_transition_filter_deviation", have_arachne);
toggle_field("wall_transition_angle", have_arachne);
toggle_field("wall_distribution_count", have_arachne);
toggle_field("wall_split_middle_threshold", have_arachne);
toggle_field("wall_add_middle_threshold", have_arachne);
toggle_field("min_feature_size", have_arachne);
toggle_field("min_bead_width", have_arachne);
toggle_field("thin_walls", !have_arachne);

View File

@ -1672,8 +1672,6 @@ void TabPrint::build()
optgroup->append_single_option_line("clip_multipart_objects");
optgroup = page->new_optgroup(L("Arachne perimeter generator"));
optgroup->append_single_option_line("wall_add_middle_threshold");
optgroup->append_single_option_line("wall_split_middle_threshold");
optgroup->append_single_option_line("wall_transition_angle");
optgroup->append_single_option_line("wall_transition_filter_deviation");
optgroup->append_single_option_line("wall_transition_length");

View File

@ -58,7 +58,7 @@ TEST_CASE("Arachne - Closed ExtrusionLine", "[ArachneClosedExtrusionLine]") {
coord_t spacing = 407079;
coord_t inset_count = 8;
Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, PrintObjectConfig::defaults(), PrintConfig::defaults());
Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, PrintObjectConfig::defaults(), PrintConfig::defaults());
wallToolPaths.generate();
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
@ -91,7 +91,7 @@ TEST_CASE("Arachne - Missing perimeter - #8472", "[ArachneMissingPerimeter8472]"
PrintObjectConfig print_object_config = PrintObjectConfig::defaults();
print_object_config.wall_distribution_count.setInt(3);
Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, print_object_config, PrintConfig::defaults());
Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, print_object_config, PrintConfig::defaults());
wallToolPaths.generate();
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();