Config.pm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. package Slic3r::Config;
  2. use strict;
  3. use warnings;
  4. use utf8;
  5. use List::Util qw(first max);
  6. # cemetery of old config settings
  7. our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration
  8. adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid
  9. rotate scale duplicate_grid start_perimeters_at_concave_points start_perimeters_at_non_overhang
  10. randomize_start seal_position bed_size print_center g0);
  11. our $Options = print_config_def();
  12. # overwrite the hard-coded readonly value (this information is not available in XS)
  13. $Options->{threads}{readonly} = !$Slic3r::have_threads;
  14. # generate accessors
  15. {
  16. no strict 'refs';
  17. for my $opt_key (keys %$Options) {
  18. *{$opt_key} = sub { $_[0]->get($opt_key) };
  19. }
  20. }
  21. sub new_from_defaults {
  22. my $class = shift;
  23. my (@opt_keys) = @_;
  24. my $self = $class->new;
  25. my $defaults = Slic3r::Config::Full->new;
  26. if (@opt_keys) {
  27. $self->set($_, $defaults->get($_)) for @opt_keys;
  28. } else {
  29. $self->apply_static($defaults);
  30. }
  31. return $self;
  32. }
  33. sub new_from_cli {
  34. my $class = shift;
  35. my %args = @_;
  36. delete $args{$_} for grep !defined $args{$_}, keys %args;
  37. for (qw(start end layer toolchange)) {
  38. my $opt_key = "${_}_gcode";
  39. if ($args{$opt_key}) {
  40. if (-e $args{$opt_key}) {
  41. Slic3r::open(\my $fh, "<", $args{$opt_key})
  42. or die "Failed to open $args{$opt_key}\n";
  43. binmode $fh, ':utf8';
  44. $args{$opt_key} = do { local $/; <$fh> };
  45. close $fh;
  46. }
  47. }
  48. }
  49. my $self = $class->new;
  50. foreach my $opt_key (keys %args) {
  51. my $opt_def = $Options->{$opt_key};
  52. # we use set_deserialize() for bool options since GetOpt::Long doesn't handle
  53. # arrays of boolean values
  54. if ($opt_key =~ /^(?:bed_shape|duplicate_grid|extruder_offset)$/ || $opt_def->{type} eq 'bool') {
  55. $self->set_deserialize($opt_key, $args{$opt_key});
  56. } elsif (my $shortcut = $opt_def->{shortcut}) {
  57. $self->set($_, $args{$opt_key}) for @$shortcut;
  58. } else {
  59. $self->set($opt_key, $args{$opt_key});
  60. }
  61. }
  62. return $self;
  63. }
  64. sub merge {
  65. my $class = shift;
  66. my $config = $class->new;
  67. $config->apply($_) for @_;
  68. return $config;
  69. }
  70. sub load {
  71. my $class = shift;
  72. my ($file) = @_;
  73. my $ini = __PACKAGE__->read_ini($file);
  74. return $class->load_ini_hash($ini->{_});
  75. }
  76. sub load_ini_hash {
  77. my $class = shift;
  78. my ($ini_hash) = @_;
  79. my $config = $class->new;
  80. foreach my $opt_key (keys %$ini_hash) {
  81. ($opt_key, my $value) = _handle_legacy($opt_key, $ini_hash->{$opt_key});
  82. next if !defined $opt_key;
  83. $config->set_deserialize($opt_key, $value);
  84. }
  85. return $config;
  86. }
  87. sub clone {
  88. my $self = shift;
  89. my $new = (ref $self)->new;
  90. $new->apply($self);
  91. return $new;
  92. }
  93. sub get_value {
  94. my $self = shift;
  95. my ($opt_key) = @_;
  96. return $Options->{$opt_key}{ratio_over}
  97. ? $self->get_abs_value($opt_key)
  98. : $self->get($opt_key);
  99. }
  100. sub _handle_legacy {
  101. my ($opt_key, $value) = @_;
  102. # handle legacy options
  103. if ($opt_key =~ /^(extrusion_width|bottom_layer_speed|first_layer_height)_ratio$/) {
  104. $opt_key = $1;
  105. $opt_key =~ s/^bottom_layer_speed$/first_layer_speed/;
  106. $value = $value =~ /^\d+(?:\.\d+)?$/ && $value != 0 ? ($value*100) . "%" : 0;
  107. }
  108. if ($opt_key eq 'threads' && !$Slic3r::have_threads) {
  109. $value = 1;
  110. }
  111. if ($opt_key eq 'gcode_flavor' && $value eq 'makerbot') {
  112. $value = 'makerware';
  113. }
  114. if ($opt_key eq 'fill_density' && defined($value) && $value !~ /%/ && $value <= 1) {
  115. # fill_density was turned into a percent value
  116. $value *= 100;
  117. $value = "$value"; # force update of the PV value, workaround for bug https://rt.cpan.org/Ticket/Display.html?id=94110
  118. }
  119. if ($opt_key eq 'randomize_start' && $value) {
  120. $opt_key = 'seam_position';
  121. $value = 'random';
  122. }
  123. if ($opt_key eq 'bed_size' && $value) {
  124. $opt_key = 'bed_shape';
  125. my ($x, $y) = split /,/, $value;
  126. $value = "0x0,${x}x0,${x}x${y},0x${y}";
  127. }
  128. return () if first { $_ eq $opt_key } @Ignore;
  129. # For historical reasons, the world's full of configs having these very low values;
  130. # to avoid unexpected behavior we need to ignore them. Banning these two hard-coded
  131. # values is a dirty hack and will need to be removed sometime in the future, but it
  132. # will avoid lots of complaints for now.
  133. if ($opt_key eq 'perimeter_acceleration' && $value == '25') {
  134. $value = 0;
  135. }
  136. if ($opt_key eq 'infill_acceleration' && $value == '50') {
  137. $value = 0;
  138. }
  139. if (!exists $Options->{$opt_key}) {
  140. my @keys = grep { $Options->{$_}{aliases} && grep $_ eq $opt_key, @{$Options->{$_}{aliases}} } keys %$Options;
  141. if (!@keys) {
  142. warn "Unknown option $opt_key\n";
  143. return ();
  144. }
  145. $opt_key = $keys[0];
  146. }
  147. return ($opt_key, $value);
  148. }
  149. sub as_ini {
  150. my ($self) = @_;
  151. my $ini = { _ => {} };
  152. foreach my $opt_key (sort @{$self->get_keys}) {
  153. next if $Options->{$opt_key}{shortcut};
  154. $ini->{_}{$opt_key} = $self->serialize($opt_key);
  155. }
  156. return $ini;
  157. }
  158. sub save {
  159. my $self = shift;
  160. my ($file) = @_;
  161. __PACKAGE__->write_ini($file, $self->as_ini);
  162. }
  163. sub setenv {
  164. my $self = shift;
  165. foreach my $opt_key (@{$self->get_keys}) {
  166. $ENV{"SLIC3R_" . uc $opt_key} = $self->serialize($opt_key);
  167. }
  168. }
  169. # this method is idempotent by design and only applies to ::DynamicConfig or ::Full
  170. # objects because it performs cross checks
  171. sub validate {
  172. my $self = shift;
  173. # -j, --threads
  174. die "Invalid value for --threads\n"
  175. if $self->threads < 1;
  176. # --layer-height
  177. die "Invalid value for --layer-height\n"
  178. if $self->layer_height <= 0;
  179. die "--layer-height must be a multiple of print resolution\n"
  180. if $self->layer_height / &Slic3r::SCALING_FACTOR % 1 != 0;
  181. # --first-layer-height
  182. die "Invalid value for --first-layer-height\n"
  183. if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/;
  184. die "Invalid value for --first-layer-height\n"
  185. if $self->get_value('first_layer_height') <= 0;
  186. # --filament-diameter
  187. die "Invalid value for --filament-diameter\n"
  188. if grep $_ < 1, @{$self->filament_diameter};
  189. # --nozzle-diameter
  190. die "Invalid value for --nozzle-diameter\n"
  191. if grep $_ < 0, @{$self->nozzle_diameter};
  192. # --perimeters
  193. die "Invalid value for --perimeters\n"
  194. if $self->perimeters < 0;
  195. # --solid-layers
  196. die "Invalid value for --solid-layers\n" if defined $self->solid_layers && $self->solid_layers < 0;
  197. die "Invalid value for --top-solid-layers\n" if $self->top_solid_layers < 0;
  198. die "Invalid value for --bottom-solid-layers\n" if $self->bottom_solid_layers < 0;
  199. # --gcode-flavor
  200. die "Invalid value for --gcode-flavor\n"
  201. if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
  202. die "--use-firmware-retraction is only supported by Marlin firmware\n"
  203. if $self->use_firmware_retraction && $self->gcode_flavor ne 'reprap' && $self->gcode_flavor ne 'machinekit';
  204. die "--use-firmware-retraction is not compatible with --wipe\n"
  205. if $self->use_firmware_retraction && first {$_} @{$self->wipe};
  206. # --fill-pattern
  207. die "Invalid value for --fill-pattern\n"
  208. if !first { $_ eq $self->fill_pattern } @{$Options->{fill_pattern}{values}};
  209. # --external-fill-pattern
  210. die "Invalid value for --external-fill-pattern\n"
  211. if !first { $_ eq $self->external_fill_pattern } @{$Options->{external_fill_pattern}{values}};
  212. # --fill-density
  213. die "The selected fill pattern is not supposed to work at 100% density\n"
  214. if $self->fill_density == 100
  215. && !first { $_ eq $self->fill_pattern } @{$Options->{external_fill_pattern}{values}};
  216. # --infill-every-layers
  217. die "Invalid value for --infill-every-layers\n"
  218. if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1;
  219. # --skirt-height
  220. die "Invalid value for --skirt-height\n"
  221. if $self->skirt_height < -1; # -1 means as tall as the object
  222. # --bridge-flow-ratio
  223. die "Invalid value for --bridge-flow-ratio\n"
  224. if $self->bridge_flow_ratio <= 0;
  225. # extruder clearance
  226. die "Invalid value for --extruder-clearance-radius\n"
  227. if $self->extruder_clearance_radius <= 0;
  228. die "Invalid value for --extruder-clearance-height\n"
  229. if $self->extruder_clearance_height <= 0;
  230. # --extrusion-multiplier
  231. die "Invalid value for --extrusion-multiplier\n"
  232. if defined first { $_ <= 0 } @{$self->extrusion_multiplier};
  233. # --default-acceleration
  234. die "Invalid zero value for --default-acceleration when using other acceleration settings\n"
  235. if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration)
  236. && !$self->default_acceleration;
  237. # --spiral-vase
  238. if ($self->spiral_vase) {
  239. # Note that we might want to have more than one perimeter on the bottom
  240. # solid layers.
  241. die "Can't make more than one perimeter when spiral vase mode is enabled\n"
  242. if $self->perimeters > 1;
  243. die "Can't make less than one perimeter when spiral vase mode is enabled\n"
  244. if $self->perimeters < 1;
  245. die "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0\n"
  246. if $self->fill_density > 0;
  247. die "Spiral vase mode is not compatible with top solid layers\n"
  248. if $self->top_solid_layers > 0;
  249. die "Spiral vase mode is not compatible with support material\n"
  250. if $self->support_material || $self->support_material_enforce_layers > 0;
  251. }
  252. # extrusion widths
  253. {
  254. my $max_nozzle_diameter = max(@{ $self->nozzle_diameter });
  255. die "Invalid extrusion width (too large)\n"
  256. if defined first { $_ > 10 * $max_nozzle_diameter }
  257. map $self->get_abs_value_over("${_}_extrusion_width", $self->layer_height),
  258. qw(perimeter infill solid_infill top_infill support_material first_layer);
  259. }
  260. # general validation, quick and dirty
  261. foreach my $opt_key (@{$self->get_keys}) {
  262. my $opt = $Options->{$opt_key};
  263. next unless defined $self->$opt_key;
  264. next unless defined $opt->{cli} && $opt->{cli} =~ /=(.+)$/;
  265. my $type = $1;
  266. my @values = ();
  267. if ($type =~ s/\@$//) {
  268. die "Invalid value for $opt_key\n" if ref($self->$opt_key) ne 'ARRAY';
  269. @values = @{ $self->$opt_key };
  270. } else {
  271. @values = ($self->$opt_key);
  272. }
  273. foreach my $value (@values) {
  274. if ($type eq 'i' || $type eq 'f' || $opt->{type} eq 'percent') {
  275. $value =~ s/%$// if $opt->{type} eq 'percent';
  276. die "Invalid value for $opt_key\n"
  277. if ($type eq 'i' && $value !~ /^-?\d+$/)
  278. || (($type eq 'f' || $opt->{type} eq 'percent') && $value !~ /^-?(?:\d+|\d*\.\d+)$/)
  279. || (defined $opt->{min} && $value < $opt->{min})
  280. || (defined $opt->{max} && $value > $opt->{max});
  281. } elsif ($type eq 's' && $opt->{type} eq 'select') {
  282. die "Invalid value for $opt_key\n"
  283. unless first { $_ eq $value } @{ $opt->{values} };
  284. }
  285. }
  286. }
  287. return 1;
  288. }
  289. # min object distance is max(duplicate_distance, clearance_radius)
  290. sub min_object_distance {
  291. my $self = shift;
  292. return ($self->complete_objects && $self->extruder_clearance_radius > $self->duplicate_distance)
  293. ? $self->extruder_clearance_radius
  294. : $self->duplicate_distance;
  295. }
  296. # CLASS METHODS:
  297. sub write_ini {
  298. my $class = shift;
  299. my ($file, $ini) = @_;
  300. Slic3r::open(\my $fh, '>', $file);
  301. binmode $fh, ':utf8';
  302. my $localtime = localtime;
  303. printf $fh "# generated by Slic3r $Slic3r::VERSION on %s\n", "$localtime";
  304. # make sure the _ category is the first one written
  305. foreach my $category (sort { ($a eq '_') ? -1 : ($a cmp $b) } keys %$ini) {
  306. printf $fh "\n[%s]\n", $category if $category ne '_';
  307. foreach my $key (sort keys %{$ini->{$category}}) {
  308. printf $fh "%s = %s\n", $key, $ini->{$category}{$key};
  309. }
  310. }
  311. close $fh;
  312. }
  313. sub read_ini {
  314. my $class = shift;
  315. my ($file) = @_;
  316. local $/ = "\n";
  317. Slic3r::open(\my $fh, '<', $file)
  318. or die "Unable to open $file: $!\n";
  319. binmode $fh, ':utf8';
  320. my $ini = { _ => {} };
  321. my $category = '_';
  322. while (<$fh>) {
  323. s/\R+$//;
  324. next if /^\s+/;
  325. next if /^$/;
  326. next if /^\s*#/;
  327. if (/^\[(.+?)\]$/) {
  328. $category = $1;
  329. next;
  330. }
  331. /^(\w+) *= *(.*)/ or die "Unreadable configuration file (invalid data at line $.)\n";
  332. $ini->{$category}{$1} = $2;
  333. }
  334. close $fh;
  335. return $ini;
  336. }
  337. package Slic3r::Config::GCode;
  338. use parent 'Slic3r::Config';
  339. package Slic3r::Config::Print;
  340. use parent 'Slic3r::Config';
  341. package Slic3r::Config::PrintObject;
  342. use parent 'Slic3r::Config';
  343. package Slic3r::Config::PrintRegion;
  344. use parent 'Slic3r::Config';
  345. package Slic3r::Config::Full;
  346. use parent 'Slic3r::Config';
  347. 1;