diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 12947eca7..133dee776 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -57,6 +57,8 @@ sub new_from_cli { foreach my $opt_key (keys %args) { if ($opt_key =~ /^(?:print_center|bed_size|duplicate_grid|extruder_offset|retract_layer_change|wipe)$/) { $self->set_deserialize($opt_key, $args{$opt_key}); + } elsif (my $shortcut = $Options->{$opt_key}{shortcut}) { + $self->set($_, $args{$opt_key}) for @$shortcut; } else { $self->set($opt_key, $args{$opt_key}); } diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 2fdf93559..19af91f48 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -528,7 +528,7 @@ sub build { $self->add_options_page('Multiple Extruders', 'funnel.png', optgroups => [ { - title => 'Extruders', + title => 'Override extruders', options => [qw(perimeter_extruder infill_extruder support_material_extruder support_material_interface_extruder)], }, { diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp index e1c520e61..74bdce1d8 100644 --- a/xs/src/Config.cpp +++ b/xs/src/Config.cpp @@ -319,37 +319,6 @@ StaticConfig::keys(t_config_option_keys *keys) const { } } -void -StaticConfig::apply(const DynamicConfig &other, bool ignore_nonexistent) { - // clone the other config so that we can remove shortcut options after applying them - DynamicConfig other_clone = other; - - // get list of option keys to apply - t_config_option_keys opt_keys; - other_clone.keys(&opt_keys); - - // loop through options and apply them - for (t_config_option_keys::const_iterator opt_key = opt_keys.begin(); opt_key != opt_keys.end(); ++opt_key) { - // if this is not a shortcut, skip it - ConfigOptionDef* optdef = &(*this->def)[*opt_key]; - if (optdef->shortcut.empty()) continue; - - // expand the option into other_clone if it does not exist already - for (std::vector::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) { - if (other_clone.has(*it)) continue; - ConfigOption* my_opt = other_clone.option(*it, true); - - // not the most efficient way, but easier than casting pointers to subclasses - my_opt->deserialize( other_clone.option(*opt_key)->serialize() ); - } - - // remove the shortcut option from other_clone - other_clone.erase(*opt_key); - } - - static_cast(this)->apply(other_clone, ignore_nonexistent); -} - const ConfigOption* StaticConfig::option(const t_config_option_key opt_key) const { diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp index e6832cf55..419b13485 100644 --- a/xs/src/Config.hpp +++ b/xs/src/Config.hpp @@ -22,6 +22,8 @@ class ConfigOption { virtual ~ConfigOption() {}; virtual std::string serialize() const = 0; virtual bool deserialize(std::string str) = 0; + virtual int getInt() const { return 0; }; + virtual void setInt(int val) {}; }; template @@ -91,6 +93,8 @@ class ConfigOptionInt : public ConfigOption ConfigOptionInt() : value(0) {}; operator int() const { return this->value; }; + int getInt() const { return this->value; }; + void setInt(int val) { this->value = val; }; std::string serialize() const { std::ostringstream ss; @@ -499,11 +503,6 @@ class StaticConfig : public ConfigBase { public: void keys(t_config_option_keys *keys) const; - void apply(const ConfigBase &other, bool ignore_nonexistent = false) { - // this proxy appears to be needed otherwise the inherited signature couldn't be found from .xsp - ConfigBase::apply(other, ignore_nonexistent); - }; - void apply(const DynamicConfig &other, bool ignore_nonexistent = false); virtual ConfigOption* option(const t_config_option_key opt_key, bool create = false) = 0; const ConfigOption* option(const t_config_option_key opt_key) const; diff --git a/xs/src/PrintConfig.cpp b/xs/src/PrintConfig.cpp index 711740897..99dd8535a 100644 --- a/xs/src/PrintConfig.cpp +++ b/xs/src/PrintConfig.cpp @@ -4,4 +4,44 @@ namespace Slic3r { t_optiondef_map PrintConfigDef::def = PrintConfigDef::build_def(); +void +StaticPrintConfig::prepare_extruder_option(const t_config_option_key opt_key, DynamicPrintConfig& other) +{ + // don't apply role-based extruders if their value is zero + if (other.has(opt_key) && other.option(opt_key)->getInt() == 0) + other.erase(opt_key); + + // only apply default extruder if our role-based value is zero + // (i.e. default extruder has the lowest priority among all other values) + if (other.has("extruder")) { + int extruder = other.option("extruder")->getInt(); + if (extruder > 0) { + if (!other.has(opt_key) && this->option(opt_key)->getInt() == 0) + other.option(opt_key, true)->setInt(extruder); + } + } +} + +void +PrintObjectConfig::apply(const ConfigBase &other, bool ignore_nonexistent) { + DynamicPrintConfig other_clone; + other_clone.apply(other); + + this->prepare_extruder_option("support_material_extruder", other_clone); + this->prepare_extruder_option("support_material_interface_extruder", other_clone); + + StaticConfig::apply(other_clone, ignore_nonexistent); +}; + +void +PrintRegionConfig::apply(const ConfigBase &other, bool ignore_nonexistent) { + DynamicPrintConfig other_clone; + other_clone.apply(other); + + this->prepare_extruder_option("infill_extruder", other_clone); + this->prepare_extruder_option("perimeter_extruder", other_clone); + + StaticConfig::apply(other_clone, ignore_nonexistent); +}; + } diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 533805693..2b0b34392 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -177,10 +177,6 @@ class PrintConfigDef Options["extruder"].tooltip = "The extruder to use (unless more specific extruder settings are specified)."; Options["extruder"].cli = "extruder=i"; Options["extruder"].min = 1; - Options["extruder"].shortcut.push_back("infill_extruder"); - Options["extruder"].shortcut.push_back("perimeter_extruder"); - Options["extruder"].shortcut.push_back("support_material_extruder"); - Options["extruder"].shortcut.push_back("support_material_interface_extruder"); Options["extruder_clearance_height"].type = coFloat; Options["extruder_clearance_height"].label = "Height"; @@ -404,8 +400,9 @@ class PrintConfigDef Options["infill_extruder"].label = "Infill extruder"; Options["infill_extruder"].category = "Extruders"; Options["infill_extruder"].tooltip = "The extruder to use when printing infill."; + Options["infill_extruder"].sidetext = "(leave 0 for default)"; Options["infill_extruder"].cli = "infill-extruder=i"; - Options["infill_extruder"].min = 1; + Options["infill_extruder"].min = 0; Options["infill_extrusion_width"].type = coFloatOrPercent; Options["infill_extrusion_width"].label = "Infill"; @@ -528,9 +525,10 @@ class PrintConfigDef Options["perimeter_extruder"].label = "Perimeter extruder"; Options["perimeter_extruder"].category = "Extruders"; Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters."; + Options["perimeter_extruder"].sidetext = "(leave 0 for default)"; Options["perimeter_extruder"].cli = "perimeter-extruder=i"; Options["perimeter_extruder"].aliases.push_back("perimeters_extruder"); - Options["perimeter_extruder"].min = 1; + Options["perimeter_extruder"].min = 0; Options["perimeter_extrusion_width"].type = coFloatOrPercent; Options["perimeter_extrusion_width"].label = "Perimeters"; @@ -777,8 +775,9 @@ class PrintConfigDef Options["support_material_extruder"].label = "Support material extruder"; Options["support_material_extruder"].category = "Extruders"; Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too."; + Options["support_material_extruder"].sidetext = "(leave 0 for default)"; Options["support_material_extruder"].cli = "support-material-extruder=i"; - Options["support_material_extruder"].min = 1; + Options["support_material_extruder"].min = 0; Options["support_material_extrusion_width"].type = coFloatOrPercent; Options["support_material_extrusion_width"].label = "Support material"; @@ -791,8 +790,9 @@ class PrintConfigDef Options["support_material_interface_extruder"].label = "Support material interface extruder"; Options["support_material_interface_extruder"].category = "Extruders"; Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too."; + Options["support_material_interface_extruder"].sidetext = "(leave 0 for default)"; Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i"; - Options["support_material_interface_extruder"].min = 1; + Options["support_material_interface_extruder"].min = 0; Options["support_material_interface_layers"].type = coInt; Options["support_material_interface_layers"].label = "Interface layers"; @@ -934,7 +934,26 @@ class PrintConfigDef }; }; -class PrintObjectConfig : public virtual StaticConfig +class DynamicPrintConfig : public DynamicConfig +{ + public: + DynamicPrintConfig() { + this->def = &PrintConfigDef::def; + }; +}; + +class StaticPrintConfig : public virtual StaticConfig +{ + public: + StaticPrintConfig() { + this->def = &PrintConfigDef::def; + }; + + protected: + void prepare_extruder_option(const t_config_option_key opt_key, DynamicPrintConfig& other); +}; + +class PrintObjectConfig : public virtual StaticPrintConfig { public: ConfigOptionFloatOrPercent extrusion_width; @@ -956,9 +975,7 @@ class PrintObjectConfig : public virtual StaticConfig ConfigOptionFloat support_material_speed; ConfigOptionInt support_material_threshold; - PrintObjectConfig() { - this->def = &PrintConfigDef::def; - + PrintObjectConfig() : StaticPrintConfig() { this->extrusion_width.value = 0; this->extrusion_width.percent = false; this->first_layer_height.value = 0.35; @@ -970,10 +987,10 @@ class PrintObjectConfig : public virtual StaticConfig this->support_material.value = false; this->support_material_angle.value = 0; this->support_material_enforce_layers.value = 0; - this->support_material_extruder.value = 1; + this->support_material_extruder.value = 0; this->support_material_extrusion_width.value = 0; this->support_material_extrusion_width.percent = false; - this->support_material_interface_extruder.value = 1; + this->support_material_interface_extruder.value = 0; this->support_material_interface_layers.value = 3; this->support_material_interface_spacing.value = 0; this->support_material_pattern.value = smpHoneycomb; @@ -1004,9 +1021,11 @@ class PrintObjectConfig : public virtual StaticConfig return NULL; }; + + void apply(const ConfigBase &other, bool ignore_nonexistent = false); }; -class PrintRegionConfig : public virtual StaticConfig +class PrintRegionConfig : public virtual StaticPrintConfig { public: ConfigOptionInt bottom_solid_layers; @@ -1028,19 +1047,17 @@ class PrintRegionConfig : public virtual StaticConfig ConfigOptionFloatOrPercent top_infill_extrusion_width; ConfigOptionInt top_solid_layers; - PrintRegionConfig() { - this->def = &PrintConfigDef::def; - + PrintRegionConfig() : StaticPrintConfig() { this->bottom_solid_layers.value = 3; this->extra_perimeters.value = true; this->fill_angle.value = 45; this->fill_density.value = 40; this->fill_pattern.value = ipHoneycomb; - this->infill_extruder.value = 1; + this->infill_extruder.value = 0; this->infill_extrusion_width.value = 0; this->infill_extrusion_width.percent = false; this->infill_every_layers.value = 1; - this->perimeter_extruder.value = 1; + this->perimeter_extruder.value = 0; this->perimeter_extrusion_width.value = 0; this->perimeter_extrusion_width.percent = false; this->perimeters.value = 3; @@ -1077,9 +1094,11 @@ class PrintRegionConfig : public virtual StaticConfig return NULL; }; + + void apply(const ConfigBase &other, bool ignore_nonexistent = false); }; -class PrintConfig : public virtual StaticConfig +class PrintConfig : public virtual StaticPrintConfig { public: ConfigOptionBool avoid_crossing_perimeters; @@ -1166,9 +1185,7 @@ class PrintConfig : public virtual StaticConfig ConfigOptionBools wipe; ConfigOptionFloat z_offset; - PrintConfig() { - this->def = &PrintConfigDef::def; - + PrintConfig() : StaticPrintConfig() { this->avoid_crossing_perimeters.value = false; this->bed_size.point = Pointf(200,200); this->bed_temperature.value = 0; @@ -1372,15 +1389,8 @@ class PrintConfig : public virtual StaticConfig } }; -class DynamicPrintConfig : public DynamicConfig -{ - public: - DynamicPrintConfig() { - this->def = &PrintConfigDef::def; - }; -}; - class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig { + public: ConfigOption* option(const t_config_option_key opt_key, bool create = false) { ConfigOption* opt; if ((opt = PrintObjectConfig::option(opt_key, create)) != NULL) return opt; @@ -1388,6 +1398,12 @@ class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, publ if ((opt = PrintConfig::option(opt_key, create)) != NULL) return opt; return NULL; }; + + void apply(const ConfigBase &other, bool ignore_nonexistent = false) { + PrintObjectConfig::apply(other, ignore_nonexistent); + PrintRegionConfig::apply(other, ignore_nonexistent); + PrintConfig::apply(other, ignore_nonexistent); + }; }; } diff --git a/xs/t/15_config.t b/xs/t/15_config.t index b3e16d387..b1e2391f2 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 95; +use Test::More tests => 99; foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set('layer_height', 0.3); @@ -127,20 +127,60 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { } { - my $config = Slic3r::Config->new; - $config->set('extruder', 2); - is $config->get('extruder'), 2, 'shortcut option value is kept in dynamic config'; - ok !$config->has('perimeter_extruder'), 'shortcut is not expanded in dynamic config'; - { - my $config2 = Slic3r::Config::Full->new; - $config2->set('extruder', 2); - is $config2->get('perimeter_extruder'), 2, 'shortcut is expanded in static config (set)'; + my $config = Slic3r::Config->new; + $config->set('infill_extruder', 2); + my $config2 = Slic3r::Config::PrintRegion->new; + $config2->apply($config); + is $config2->get('infill_extruder'), $config->get('infill_extruder'), + 'non-zero infill_extruder is applied'; } { - my $config3 = Slic3r::Config::Full->new; - $config3->apply_dynamic($config); - is $config3->get('perimeter_extruder'), 2, 'shortcut is expanded in static config (apply)'; + my $config = Slic3r::Config->new; + $config->set('infill_extruder', 0); + my $config2 = Slic3r::Config::PrintRegion->new; + $config2->set('infill_extruder', 3); + $config2->apply($config); + isnt $config2->get('infill_extruder'), $config->get('infill_extruder'), + 'zero infill_extruder is not applied'; + } + { + my $config = Slic3r::Config->new; + $config->set('extruder', 3); + my $config2 = Slic3r::Config::PrintRegion->new; + $config2->set('infill_extruder', 2); + $config2->apply($config); + is $config2->get('infill_extruder'), 2, 'extruder does not overwrite non-zero role extruders'; + is $config2->get('perimeter_extruder'), 3, 'extruder overwrites zero role extruders'; + } +} + +{ + { + my $config = Slic3r::Config->new; + $config->set('support_material_extruder', 2); + my $config2 = Slic3r::Config::PrintObject->new; + $config2->apply($config); + is $config2->get('support_material_extruder'), $config->get('support_material_extruder'), + 'non-zero support_material_extruder is applied'; + } + { + my $config = Slic3r::Config->new; + $config->set('support_material_extruder', 0); + my $config2 = Slic3r::Config::PrintObject->new; + $config2->set('support_material_extruder', 3); + $config2->apply($config); + isnt $config2->get('support_material_extruder'), $config->get('support_material_extruder'), + 'zero support_material_extruder is not applied'; + } + { + my $config = Slic3r::Config->new; + $config->set('extruder', 3); + my $config2 = Slic3r::Config::PrintObject->new; + $config2->set('support_material_extruder', 2); + $config2->apply($config); + is $config2->get('support_material_extruder'), 2, 'extruder does not overwrite non-zero role extruders'; + is $config2->get('support_material_interface_extruder'), 3, 'extruder overwrites zero role extruders'; } } diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 61490f967..b7cb8ea3a 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -102,8 +102,6 @@ double get_abs_value(t_config_option_key opt_key); %name{get_abs_value_over} double get_abs_value(t_config_option_key opt_key, double ratio_over); - void apply(PrintObjectConfig* other) - %code{% THIS->apply(*other, true); %}; void apply_dynamic(DynamicPrintConfig* other) %code{% THIS->apply(*other, true); %}; std::vector get_keys()