1
package Slic3r::Config;
6
use List::Util qw(first max);
8
# cemetery of old config settings
9
our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration
10
adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid
11
rotate scale duplicate_grid start_perimeters_at_concave_points start_perimeters_at_non_overhang
12
randomize_start seal_position);
14
our $Options = print_config_def();
16
# overwrite the hard-coded readonly value (this information is not available in XS)
17
$Options->{threads}{readonly} = !$Slic3r::have_threads;
22
for my $opt_key (keys %$Options) {
23
*{$opt_key} = sub { $_[0]->get($opt_key) };
27
sub new_from_defaults {
31
my $self = $class->new;
32
my $defaults = Slic3r::Config::Full->new;
34
$self->set($_, $defaults->get($_)) for @opt_keys;
36
$self->apply_static($defaults);
45
delete $args{$_} for grep !defined $args{$_}, keys %args;
47
for (qw(start end layer toolchange)) {
48
my $opt_key = "${_}_gcode";
49
if ($args{$opt_key}) {
50
if (-e $args{$opt_key}) {
51
Slic3r::open(\my $fh, "<", $args{$opt_key})
52
or die "Failed to open $args{$opt_key}\n";
54
$args{$opt_key} = do { local $/; <$fh> };
60
my $self = $class->new;
61
foreach my $opt_key (keys %args) {
62
my $opt_def = $Options->{$opt_key};
64
# we use set_deserialize() for bool options since GetOpt::Long doesn't handle
65
# arrays of boolean values
66
if ($opt_key =~ /^(?:print_center|bed_size|duplicate_grid|extruder_offset)$/ || $opt_def->{type} eq 'bool') {
67
$self->set_deserialize($opt_key, $args{$opt_key});
68
} elsif (my $shortcut = $opt_def->{shortcut}) {
69
$self->set($_, $args{$opt_key}) for @$shortcut;
71
$self->set($opt_key, $args{$opt_key});
80
my $config = $class->new;
81
$config->apply($_) for @_;
89
my $ini = __PACKAGE__->read_ini($file);
90
return $class->load_ini_hash($ini->{_});
97
my $config = $class->new;
98
foreach my $opt_key (keys %$ini_hash) {
99
($opt_key, my $value) = _handle_legacy($opt_key, $ini_hash->{$opt_key});
100
next if !defined $opt_key;
101
$config->set_deserialize($opt_key, $value);
109
my $new = (ref $self)->new;
118
return $Options->{$opt_key}{ratio_over}
119
? $self->get_abs_value($opt_key)
120
: $self->get($opt_key);
124
my ($opt_key, $value) = @_;
126
# handle legacy options
127
return () if first { $_ eq $opt_key } @Ignore;
128
if ($opt_key =~ /^(extrusion_width|bottom_layer_speed|first_layer_height)_ratio$/) {
130
$opt_key =~ s/^bottom_layer_speed$/first_layer_speed/;
131
$value = $value =~ /^\d+(?:\.\d+)?$/ && $value != 0 ? ($value*100) . "%" : 0;
133
if ($opt_key eq 'threads' && !$Slic3r::have_threads) {
136
if ($opt_key eq 'gcode_flavor' && $value eq 'makerbot') {
137
$value = 'makerware';
139
if ($opt_key eq 'fill_density' && defined($value) && $value !~ /%/ && $value <= 1) {
140
# fill_density was turned into a percent value
142
$value = "$value"; # force update of the PV value, workaround for bug https://rt.cpan.org/Ticket/Display.html?id=94110
144
if ($opt_key eq 'randomize_start' && $value) {
145
$opt_key = 'seam_position';
149
# For historical reasons, the world's full of configs having these very low values;
150
# to avoid unexpected behavior we need to ignore them. Banning these two hard-coded
151
# values is a dirty hack and will need to be removed sometime in the future, but it
152
# will avoid lots of complaints for now.
153
if ($opt_key eq 'perimeter_acceleration' && $value == '25') {
156
if ($opt_key eq 'infill_acceleration' && $value == '50') {
160
if (!exists $Options->{$opt_key}) {
161
my @keys = grep { $Options->{$_}{aliases} && grep $_ eq $opt_key, @{$Options->{$_}{aliases}} } keys %$Options;
163
warn "Unknown option $opt_key\n";
169
return ($opt_key, $value);
174
my ($opt_key, $value, $deserialize) = @_;
176
if (!$self->has($opt_key)) {
178
$self->set_deserialize($opt_key, $value);
180
$self->set($opt_key, $value);
188
my $ini = { _ => {} };
189
foreach my $opt_key (sort @{$self->get_keys}) {
190
next if $Options->{$opt_key}{shortcut};
191
$ini->{_}{$opt_key} = $self->serialize($opt_key);
200
__PACKAGE__->write_ini($file, $self->as_ini);
206
foreach my $opt_key (@{$self->get_keys}) {
207
$ENV{"SLIC3R_" . uc $opt_key} = $self->serialize($opt_key);
212
my ($self, $other) = @_;
213
return @{ $self->diff($other) } == 0;
216
# this will *ignore* options not present in both configs
218
my ($self, $other) = @_;
221
foreach my $opt_key (sort @{$self->get_keys}) {
223
if $other->has($opt_key) && $other->serialize($opt_key) ne $self->serialize($opt_key);
228
# this method is idempotent by design and only applies to ::DynamicConfig or ::Full
229
# objects because it performs cross checks
234
die "Invalid value for --threads\n"
235
if $self->threads < 1;
238
die "Invalid value for --layer-height\n"
239
if $self->layer_height <= 0;
240
die "--layer-height must be a multiple of print resolution\n"
241
if $self->layer_height / &Slic3r::SCALING_FACTOR % 1 != 0;
243
# --first-layer-height
244
die "Invalid value for --first-layer-height\n"
245
if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/;
247
# --filament-diameter
248
die "Invalid value for --filament-diameter\n"
249
if grep $_ < 1, @{$self->filament_diameter};
252
die "Invalid value for --nozzle-diameter\n"
253
if grep $_ < 0, @{$self->nozzle_diameter};
254
die "--layer-height can't be greater than --nozzle-diameter\n"
255
if grep $self->layer_height > $_, @{$self->nozzle_diameter};
256
die "First layer height can't be greater than --nozzle-diameter\n"
257
if grep $self->get_value('first_layer_height') > $_, @{$self->nozzle_diameter};
260
die "Invalid value for --perimeters\n"
261
if $self->perimeters < 0;
264
die "Invalid value for --solid-layers\n" if defined $self->solid_layers && $self->solid_layers < 0;
265
die "Invalid value for --top-solid-layers\n" if $self->top_solid_layers < 0;
266
die "Invalid value for --bottom-solid-layers\n" if $self->bottom_solid_layers < 0;
269
die "Invalid value for --gcode-flavor\n"
270
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
272
die "--use-firmware-retraction is only supported by Marlin firmware\n"
273
if $self->use_firmware_retraction && $self->gcode_flavor ne 'reprap';
275
die "--use-firmware-retraction is not compatible with --wipe\n"
276
if $self->use_firmware_retraction && first {$_} @{$self->wipe};
279
die "Invalid value for --print-center\n"
280
if !ref $self->print_center
281
&& (!$self->print_center || $self->print_center !~ /^\d+,\d+$/);
284
die "Invalid value for --fill-pattern\n"
285
if !first { $_ eq $self->fill_pattern } @{$Options->{fill_pattern}{values}};
287
# --solid-fill-pattern
288
die "Invalid value for --solid-fill-pattern\n"
289
if !first { $_ eq $self->solid_fill_pattern } @{$Options->{solid_fill_pattern}{values}};
292
die "The selected fill pattern is not supposed to work at 100% density\n"
293
if $self->fill_density == 100
294
&& !first { $_ eq $self->fill_pattern } @{$Options->{solid_fill_pattern}{values}};
296
# --infill-every-layers
297
die "Invalid value for --infill-every-layers\n"
298
if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1;
301
die "Invalid value for --bed-size\n"
302
if !ref $self->bed_size
303
&& (!$self->bed_size || $self->bed_size !~ /^\d+,\d+$/);
306
die "Invalid value for --skirt-height\n"
307
if $self->skirt_height < -1; # -1 means as tall as the object
309
# --bridge-flow-ratio
310
die "Invalid value for --bridge-flow-ratio\n"
311
if $self->bridge_flow_ratio <= 0;
314
die "Invalid value for --extruder-clearance-radius\n"
315
if $self->extruder_clearance_radius <= 0;
316
die "Invalid value for --extruder-clearance-height\n"
317
if $self->extruder_clearance_height <= 0;
319
# --extrusion-multiplier
320
die "Invalid value for --extrusion-multiplier\n"
321
if defined first { $_ <= 0 } @{$self->extrusion_multiplier};
323
# --default-acceleration
324
die "Invalid zero value for --default-acceleration when using other acceleration settings\n"
325
if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration)
326
&& !$self->default_acceleration;
329
if ($self->spiral_vase) {
330
# Note that we might want to have more than one perimeter on the bottom
332
die "Can't make more than one perimeter when spiral vase mode is enabled\n"
333
if $self->perimeters > 1;
335
die "Can't make less than one perimeter when spiral vase mode is enabled\n"
336
if $self->perimeters < 1;
338
die "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0\n"
339
if $self->fill_density > 0;
341
die "Spiral vase mode is not compatible with top solid layers\n"
342
if $self->top_solid_layers > 0;
344
die "Spiral vase mode is not compatible with support material\n"
345
if $self->support_material || $self->support_material_enforce_layers > 0;
350
my $max_nozzle_diameter = max(@{ $self->nozzle_diameter });
351
die "Invalid extrusion width (too large)\n"
352
if defined first { $_ > 10 * $max_nozzle_diameter }
353
map $self->get_abs_value_over("${_}_extrusion_width", $self->layer_height),
354
qw(perimeter infill solid_infill top_infill support_material first_layer);
357
# general validation, quick and dirty
358
foreach my $opt_key (@{$self->get_keys}) {
359
my $opt = $Options->{$opt_key};
360
next unless defined $self->$opt_key;
361
next unless defined $opt->{cli} && $opt->{cli} =~ /=(.+)$/;
364
if ($type =~ s/\@$//) {
365
die "Invalid value for $opt_key\n" if ref($self->$opt_key) ne 'ARRAY';
366
@values = @{ $self->$opt_key };
368
@values = ($self->$opt_key);
370
foreach my $value (@values) {
371
if ($type eq 'i' || $type eq 'f' || $opt->{type} eq 'percent') {
372
$value =~ s/%$// if $opt->{type} eq 'percent';
373
die "Invalid value for $opt_key\n"
374
if ($type eq 'i' && $value !~ /^-?\d+$/)
375
|| (($type eq 'f' || $opt->{type} eq 'percent') && $value !~ /^-?(?:\d+|\d*\.\d+)$/)
376
|| (defined $opt->{min} && $value < $opt->{min})
377
|| (defined $opt->{max} && $value > $opt->{max});
378
} elsif ($type eq 's' && $opt->{type} eq 'select') {
379
die "Invalid value for $opt_key\n"
380
unless first { $_ eq $value } @{ $opt->{values} };
388
# min object distance is max(duplicate_distance, clearance_radius)
389
sub min_object_distance {
392
return ($self->complete_objects && $self->extruder_clearance_radius > $self->duplicate_distance)
393
? $self->extruder_clearance_radius
394
: $self->duplicate_distance;
401
my ($file, $ini) = @_;
403
Slic3r::open(\my $fh, '>', $file);
404
binmode $fh, ':utf8';
405
my $localtime = localtime;
406
printf $fh "# generated by Slic3r $Slic3r::VERSION on %s\n", "$localtime";
407
# make sure the _ category is the first one written
408
foreach my $category (sort { ($a eq '_') ? -1 : ($a cmp $b) } keys %$ini) {
409
printf $fh "\n[%s]\n", $category if $category ne '_';
410
foreach my $key (sort keys %{$ini->{$category}}) {
411
printf $fh "%s = %s\n", $key, $ini->{$category}{$key};
422
Slic3r::open(\my $fh, '<', $file);
423
binmode $fh, ':utf8';
425
my $ini = { _ => {} };
436
/^(\w+) = (.*)/ or die "Unreadable configuration file (invalid data at line $.)\n";
437
$ini->{$category}{$1} = $2;
444
package Slic3r::Config::Print;
445
use parent 'Slic3r::Config';
447
package Slic3r::Config::PrintObject;
448
use parent 'Slic3r::Config';
450
package Slic3r::Config::PrintRegion;
451
use parent 'Slic3r::Config';
453
package Slic3r::Config::Full;
454
use parent 'Slic3r::Config';