mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-31 04:01:59 +08:00
SPE-2400: Add an option for setting the gap size between the beginning and the end of closed perimeters.
Negative values extend the loop, causing the endpoints to overlap slightly.
This commit is contained in:
parent
6cc7ff754c
commit
56a2a5973d
@ -69,6 +69,8 @@ namespace Slic3r {
|
||||
double value;
|
||||
bool percent;
|
||||
|
||||
double get_abs_value(double ratio_over) const { return this->percent ? (ratio_over * this->value / 100) : this->value; }
|
||||
|
||||
private:
|
||||
friend class cereal::access;
|
||||
template<class Archive> void serialize(Archive& ar) { ar(this->value); ar(this->percent); }
|
||||
@ -2265,17 +2267,18 @@ public:
|
||||
bool is_scalar() const { return (int(this->type) & int(coVectorType)) == 0; }
|
||||
|
||||
template<class Archive> ConfigOption* load_option_from_archive(Archive &archive) const {
|
||||
if (this->nullable) {
|
||||
switch (this->type) {
|
||||
case coFloat: { auto opt = new ConfigOptionFloatNullable(); archive(*opt); return opt; }
|
||||
case coInt: { auto opt = new ConfigOptionIntNullable(); archive(*opt); return opt; }
|
||||
case coFloats: { auto opt = new ConfigOptionFloatsNullable(); archive(*opt); return opt; }
|
||||
case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; }
|
||||
case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; }
|
||||
case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; }
|
||||
default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
} else {
|
||||
if (this->nullable) {
|
||||
switch (this->type) {
|
||||
case coFloat: { auto opt = new ConfigOptionFloatNullable(); archive(*opt); return opt; }
|
||||
case coInt: { auto opt = new ConfigOptionIntNullable(); archive(*opt); return opt; }
|
||||
case coFloats: { auto opt = new ConfigOptionFloatsNullable(); archive(*opt); return opt; }
|
||||
case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; }
|
||||
case coPercents: { auto opt = new ConfigOptionPercentsNullable(); archive(*opt); return opt; }
|
||||
case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; }
|
||||
case coFloatsOrPercents: { auto opt = new ConfigOptionFloatsOrPercentsNullable(); archive(*opt); return opt; }
|
||||
default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
} else {
|
||||
switch (this->type) {
|
||||
case coFloat: { auto opt = new ConfigOptionFloat(); archive(*opt); return opt; }
|
||||
case coFloats: { auto opt = new ConfigOptionFloats(); archive(*opt); return opt; }
|
||||
@ -2300,16 +2303,17 @@ public:
|
||||
}
|
||||
|
||||
template<class Archive> ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const {
|
||||
if (this->nullable) {
|
||||
switch (this->type) {
|
||||
case coFloat: archive(*static_cast<const ConfigOptionFloatNullable*>(opt)); break;
|
||||
case coInt: archive(*static_cast<const ConfigOptionIntNullable*>(opt)); break;
|
||||
case coFloats: archive(*static_cast<const ConfigOptionFloatsNullable*>(opt)); break;
|
||||
case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
|
||||
case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break;
|
||||
case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(opt)); break;
|
||||
default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
if (this->nullable) {
|
||||
switch (this->type) {
|
||||
case coFloat: archive(*static_cast<const ConfigOptionFloatNullable*>(opt)); break;
|
||||
case coInt: archive(*static_cast<const ConfigOptionIntNullable*>(opt)); break;
|
||||
case coFloats: archive(*static_cast<const ConfigOptionFloatsNullable*>(opt)); break;
|
||||
case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
|
||||
case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt)); break;
|
||||
case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(opt)); break;
|
||||
case coFloatsOrPercents: archive(*static_cast<const ConfigOptionFloatsOrPercentsNullable*>(opt)); break;
|
||||
default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
} else {
|
||||
switch (this->type) {
|
||||
case coFloat: archive(*static_cast<const ConfigOptionFloat*>(opt)); break;
|
||||
|
@ -2311,6 +2311,17 @@ std::pair<GCode::SmoothPath, std::size_t> split_with_seam(
|
||||
}
|
||||
} // namespace GCode
|
||||
|
||||
static inline double get_seam_gap_distance_value(const PrintConfig &config, const unsigned extruder_id)
|
||||
{
|
||||
const double nozzle_diameter = config.nozzle_diameter.get_at(extruder_id);
|
||||
const FloatOrPercent seam_gap_distance_override = config.filament_seam_gap_distance.get_at(extruder_id);
|
||||
if (!std::isnan(seam_gap_distance_override.value)) {
|
||||
return seam_gap_distance_override.get_abs_value(nozzle_diameter);
|
||||
}
|
||||
|
||||
return config.seam_gap_distance.get_abs_value(nozzle_diameter);
|
||||
}
|
||||
|
||||
using GCode::ExtrusionOrder::InstancePoint;
|
||||
|
||||
struct SmoothPathGenerator
|
||||
@ -2361,12 +2372,20 @@ struct SmoothPathGenerator
|
||||
// Clip the path to avoid the extruder to get exactly on the first point of the
|
||||
// loop; if polyline was shorter than the clipping distance we'd get a null
|
||||
// polyline, so we discard it in that case.
|
||||
const auto nozzle_diameter{config.nozzle_diameter.get_at(extruder_id)};
|
||||
if (enable_loop_clipping) {
|
||||
if (const double extrusion_clipping = get_seam_gap_distance_value(config, extruder_id); enable_loop_clipping && extrusion_clipping > 0.) {
|
||||
clip_end(
|
||||
result, scale_(nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER,
|
||||
result,
|
||||
scaled<double>(extrusion_clipping),
|
||||
scaled<double>(GCode::ExtrusionOrder::min_gcode_segment_length)
|
||||
);
|
||||
} else if (enable_loop_clipping && extrusion_clipping < 0.) {
|
||||
// Extend the extrusion slightly after the seam.
|
||||
const double smooth_path_extension_length = -1. * scaled<double>(extrusion_clipping);
|
||||
const double smooth_path_extension_cut_length = length(result) - smooth_path_extension_length;
|
||||
GCode::SmoothPath smooth_path_extension = result;
|
||||
|
||||
clip_end(smooth_path_extension, smooth_path_extension_cut_length, scaled<double>(GCode::ExtrusionOrder::min_gcode_segment_length));
|
||||
Slic3r::append(result, smooth_path_extension);
|
||||
}
|
||||
|
||||
assert(validate_smooth_path(result, !enable_loop_clipping));
|
||||
|
@ -456,7 +456,8 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
|
||||
"ensure_vertical_shell_thickness", "extra_perimeters", "extra_perimeters_on_overhangs",
|
||||
"avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs",
|
||||
"seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
|
||||
"seam_position", "staggered_inner_seams", "seam_gap_distance",
|
||||
"external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
|
||||
"scarf_seam_placement", "scarf_seam_only_on_smooth", "scarf_seam_start_height", "scarf_seam_entire_loop", "scarf_seam_length", "scarf_seam_max_segment_length", "scarf_seam_on_inner_perimeters",
|
||||
"infill_every_layers", /*"infill_only_where_needed",*/ "solid_infill_every_layers", "fill_angle", "bridge_angle",
|
||||
"solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",
|
||||
@ -512,6 +513,8 @@ static std::vector<std::string> s_Preset_filament_options {
|
||||
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
// Shrinkage compensation
|
||||
"filament_shrinkage_compensation_xy", "filament_shrinkage_compensation_z",
|
||||
// Seams overrides
|
||||
"filament_seam_gap_distance"
|
||||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_machine_limits_options {
|
||||
|
@ -120,6 +120,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"filament_density",
|
||||
"filament_notes",
|
||||
"filament_cost",
|
||||
"filament_seam_gap_distance",
|
||||
"filament_spool_weight",
|
||||
"first_layer_acceleration",
|
||||
"first_layer_acceleration_over_raft",
|
||||
@ -161,6 +162,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"retract_restart_extra",
|
||||
"retract_restart_extra_toolchange",
|
||||
"retract_speed",
|
||||
"seam_gap_distance",
|
||||
"single_extruder_multi_material_priming",
|
||||
"slowdown_below_layer_time",
|
||||
"solid_infill_acceleration",
|
||||
|
@ -2654,6 +2654,17 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloats { 0. });
|
||||
|
||||
def = this->add("seam_gap_distance", coFloatOrPercent);
|
||||
def->label = L("Seam gap distance");
|
||||
def->tooltip = L("The distance between the endpoints of a closed loop perimeter. "
|
||||
"Positive values will shorten and interrupt the loop slightly to reduce the seam. "
|
||||
"Negative values will extend the loop, causing the endpoints to overlap slightly. "
|
||||
"When percents are used, the distance is derived from the nozzle diameter. "
|
||||
"Set to zero to disable this feature.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent{ 15., true });
|
||||
|
||||
def = this->add("seam_position", coEnum);
|
||||
def->label = L("Seam position");
|
||||
def->category = L("Layers and Perimeters");
|
||||
@ -3775,9 +3786,9 @@ void PrintConfigDef::init_fff_params()
|
||||
auto it_opt = options.find(opt_key);
|
||||
assert(it_opt != options.end());
|
||||
def = this->add_nullable(std::string("filament_") + opt_key, it_opt->second.type);
|
||||
def->label = it_opt->second.label;
|
||||
def->label = it_opt->second.label;
|
||||
def->full_label = it_opt->second.full_label;
|
||||
def->tooltip = it_opt->second.tooltip;
|
||||
def->tooltip = it_opt->second.tooltip;
|
||||
def->sidetext = it_opt->second.sidetext;
|
||||
def->mode = it_opt->second.mode;
|
||||
switch (def->type) {
|
||||
@ -3787,6 +3798,44 @@ void PrintConfigDef::init_fff_params()
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Declare values for filament profile, overriding printer's profile.
|
||||
for (const char *opt_key : {
|
||||
// Floats or Percents
|
||||
"seam_gap_distance"}) {
|
||||
|
||||
auto it_opt = options.find(opt_key);
|
||||
assert(it_opt != options.end());
|
||||
|
||||
switch (it_opt->second.type) {
|
||||
case coFloatOrPercent: {
|
||||
def = this->add_nullable(std::string("filament_") + opt_key, coFloatsOrPercents);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
def->label = it_opt->second.label;
|
||||
def->full_label = it_opt->second.full_label;
|
||||
def->tooltip = it_opt->second.tooltip;
|
||||
def->sidetext = it_opt->second.sidetext;
|
||||
def->mode = it_opt->second.mode;
|
||||
|
||||
switch (def->type) {
|
||||
case coFloatsOrPercents: {
|
||||
const auto &default_value = *static_cast<const ConfigOptionFloatOrPercent *>(it_opt->second.default_value.get());
|
||||
def->set_default_value(new ConfigOptionFloatsOrPercentsNullable{{default_value.value, default_value.percent}});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintConfigDef::init_extruder_option_keys()
|
||||
|
@ -830,6 +830,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloats, filament_multitool_ramming_flow))
|
||||
((ConfigOptionFloats, filament_stamping_loading_speed))
|
||||
((ConfigOptionFloats, filament_stamping_distance))
|
||||
((ConfigOptionFloatsOrPercentsNullable, filament_seam_gap_distance))
|
||||
((ConfigOptionPercents, filament_shrinkage_compensation_xy))
|
||||
((ConfigOptionPercents, filament_shrinkage_compensation_z))
|
||||
((ConfigOptionBool, gcode_comments))
|
||||
@ -860,6 +861,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloats, retract_restart_extra))
|
||||
((ConfigOptionFloats, retract_restart_extra_toolchange))
|
||||
((ConfigOptionFloats, retract_speed))
|
||||
((ConfigOptionFloatOrPercent, seam_gap_distance))
|
||||
((ConfigOptionString, start_gcode))
|
||||
((ConfigOptionStrings, start_filament_gcode))
|
||||
((ConfigOptionBool, single_extruder_multi_material))
|
||||
|
@ -331,19 +331,22 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
||||
if ((m_opt.type == coFloatOrPercent || m_opt.type == coFloatsOrPercents) && !str.IsEmpty() && str.Last() != '%')
|
||||
{
|
||||
double val = 0.;
|
||||
|
||||
bool is_na_value = m_opt.nullable && str == na_value();
|
||||
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
// Replace the first incorrect separator in decimal number.
|
||||
if (str.Replace(dec_sep_alt, dec_sep, false) != 0)
|
||||
if (!is_na_value && str.Replace(dec_sep_alt, dec_sep, false) != 0)
|
||||
set_value(str, false);
|
||||
|
||||
|
||||
// remove space and "mm" substring, if any exists
|
||||
str.Replace(" ", "", true);
|
||||
str.Replace("m", "", true);
|
||||
|
||||
if (!str.ToDouble(&val))
|
||||
{
|
||||
if (is_na_value) {
|
||||
val = ConfigOptionFloatsOrPercentsNullable::nil_value().value;
|
||||
} else if (!str.ToDouble(&val)) {
|
||||
if (!check_value) {
|
||||
m_value.clear();
|
||||
break;
|
||||
@ -453,8 +456,11 @@ void TextCtrl::BUILD() {
|
||||
case coFloatsOrPercents: {
|
||||
const auto val = m_opt.get_default_value<ConfigOptionFloatsOrPercents>()->get_at(m_opt_idx);
|
||||
text_value = double_to_string(val.value);
|
||||
if (val.percent)
|
||||
if (val.percent) {
|
||||
text_value += "%";
|
||||
}
|
||||
|
||||
m_last_meaningful_value = text_value;
|
||||
break;
|
||||
}
|
||||
case coPercent:
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include "slic3r/GUI/Search.hpp" // IWYU pragma: keep
|
||||
#include "slic3r/GUI/Field.hpp"
|
||||
#include "libslic3r/Exception.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
@ -175,8 +176,10 @@ void OptionsGroup::change_opt_value(DynamicPrintConfig& config, const t_config_o
|
||||
str.pop_back();
|
||||
percent = true;
|
||||
}
|
||||
double val = std::stod(str); // locale-dependent (on purpose - the input is the actual content of the field)
|
||||
ConfigOptionFloatsOrPercents* vec_new = new ConfigOptionFloatsOrPercents({ {val, percent} });
|
||||
|
||||
const bool is_na_value = opt_def->nullable && str == _(L("N/A"));
|
||||
const FloatOrPercent val = is_na_value ? ConfigOptionFloatsOrPercentsNullable::nil_value() : FloatOrPercent{std::stod(str), percent};
|
||||
ConfigOptionFloatsOrPercents *vec_new = new ConfigOptionFloatsOrPercents({val});
|
||||
config.option<ConfigOptionFloatsOrPercents>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
||||
break;
|
||||
}
|
||||
@ -1020,6 +1023,21 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
||||
ret = double_to_string(val); }
|
||||
}
|
||||
break;
|
||||
case coFloatsOrPercents: {
|
||||
if (config.option(opt_key)->is_nil()) {
|
||||
ret = _(L("N/A"));
|
||||
} else {
|
||||
const auto &config_option = config.option<ConfigOptionFloatsOrPercentsNullable>(opt_key)->get_at(idx);
|
||||
|
||||
text_value = double_to_string(config_option.value);
|
||||
if (config_option.percent) {
|
||||
text_value += "%";
|
||||
}
|
||||
|
||||
ret = text_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case coBools:
|
||||
ret = config.option<ConfigOptionBoolsNullable>(opt_key)->values[idx];
|
||||
break;
|
||||
|
@ -1458,6 +1458,7 @@ void TabPrint::build()
|
||||
|
||||
optgroup = page->new_optgroup(L("Advanced"));
|
||||
optgroup->append_single_option_line("seam_position", category_path + "seam-position");
|
||||
optgroup->append_single_option_line("seam_gap_distance", category_path + "seam-gap-distance");
|
||||
optgroup->append_single_option_line("staggered_inner_seams", category_path + "staggered-inner-seams");
|
||||
|
||||
optgroup->append_single_option_line("scarf_seam_placement", category_path + "scarf-seam-placement");
|
||||
@ -1968,6 +1969,9 @@ std::vector<std::pair<std::string, std::vector<std::string>>> filament_overrides
|
||||
{"Retraction when tool is disabled", {
|
||||
"filament_retract_length_toolchange",
|
||||
"filament_retract_restart_extra_toolchange"
|
||||
}},
|
||||
{"Seams", {
|
||||
"filament_seam_gap_distance"
|
||||
}}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user