Config.pm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. # Extends C++ class Slic3r::DynamicPrintConfig
  2. # This perl class does not keep any perl class variables,
  3. # all the storage is handled by the underlying C++ code.
  4. package Slic3r::Config;
  5. use strict;
  6. use warnings;
  7. use utf8;
  8. use List::Util qw(first max);
  9. # C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes.
  10. # The C++ counterpart is a constant singleton.
  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. # Fill in the underlying C++ Slic3r::DynamicPrintConfig with the content of the defaults
  22. # provided by the C++ class Slic3r::FullPrintConfig.
  23. sub new_from_defaults {
  24. my $class = shift;
  25. my (@opt_keys) = @_;
  26. my $self = $class->new;
  27. if (@opt_keys) {
  28. $self->set($_, $Options->{$_}{default})
  29. for grep exists $Options->{$_}{default}, @opt_keys;
  30. } else {
  31. $self->apply_static(Slic3r::Config::Full->new);
  32. }
  33. return $self;
  34. }
  35. # From command line parameters
  36. sub new_from_cli {
  37. my $class = shift;
  38. my %args = @_;
  39. # Delete hash keys with undefined value.
  40. delete $args{$_} for grep !defined $args{$_}, keys %args;
  41. # Replace the start_gcode, end_gcode ... hash values
  42. # with the content of the files they reference.
  43. for (qw(start end layer toolchange)) {
  44. my $opt_key = "${_}_gcode";
  45. if ($args{$opt_key}) {
  46. if (-e $args{$opt_key}) {
  47. Slic3r::open(\my $fh, "<", $args{$opt_key})
  48. or die "Failed to open $args{$opt_key}\n";
  49. binmode $fh, ':utf8';
  50. $args{$opt_key} = do { local $/; <$fh> };
  51. close $fh;
  52. }
  53. }
  54. }
  55. my $self = $class->new;
  56. foreach my $opt_key (keys %args) {
  57. my $opt_def = $Options->{$opt_key};
  58. # we use set_deserialize() for bool options since GetOpt::Long doesn't handle
  59. # arrays of boolean values
  60. if ($opt_key =~ /^(?:bed_shape|duplicate_grid|extruder_offset)$/ || $opt_def->{type} eq 'bool') {
  61. $self->set_deserialize($opt_key, $args{$opt_key});
  62. } elsif (my $shortcut = $opt_def->{shortcut}) {
  63. $self->set($_, $args{$opt_key}) for @$shortcut;
  64. } else {
  65. $self->set($opt_key, $args{$opt_key});
  66. }
  67. }
  68. return $self;
  69. }
  70. sub merge {
  71. my $class = shift;
  72. my $config = $class->new;
  73. $config->apply($_) for @_;
  74. return $config;
  75. }
  76. # Load a flat ini file without a category into the underlying C++ Slic3r::DynamicConfig class,
  77. # convert legacy configuration names.
  78. sub load {
  79. my $class = shift;
  80. my ($file) = @_;
  81. # legacy syntax of load()
  82. my $config = $class->new;
  83. $config->_load($file);
  84. return $config;
  85. }
  86. sub save {
  87. my $self = shift;
  88. my ($file) = @_;
  89. return $self->_save($file);
  90. }
  91. # Deserialize a perl hash into the underlying C++ Slic3r::DynamicConfig class,
  92. # convert legacy configuration names.
  93. sub load_ini_hash {
  94. my $class = shift;
  95. my ($ini_hash) = @_;
  96. my $config = $class->new;
  97. $config->set_deserialize($_, $ini_hash->{$_}) for keys %$ini_hash;
  98. return $config;
  99. }
  100. sub clone {
  101. my $self = shift;
  102. my $new = (ref $self)->new;
  103. $new->apply($self);
  104. return $new;
  105. }
  106. sub get_value {
  107. my $self = shift;
  108. my ($opt_key) = @_;
  109. return $Options->{$opt_key}{ratio_over}
  110. ? $self->get_abs_value($opt_key)
  111. : $self->get($opt_key);
  112. }
  113. # Create a hash of hashes from the underlying C++ Slic3r::DynamicPrintConfig.
  114. # The first hash key is '_' meaning no category.
  115. sub as_ini {
  116. my ($self) = @_;
  117. my $ini = { _ => {} };
  118. foreach my $opt_key (sort @{$self->get_keys}) {
  119. next if $Options->{$opt_key}{shortcut};
  120. $ini->{_}{$opt_key} = $self->serialize($opt_key);
  121. }
  122. return $ini;
  123. }
  124. # this method is idempotent by design and only applies to ::DynamicConfig or ::Full
  125. # objects because it performs cross checks
  126. sub validate {
  127. my $self = shift;
  128. # -j, --threads
  129. die "Invalid value for --threads\n"
  130. if $self->threads < 1;
  131. # --layer-height
  132. die "Invalid value for --layer-height\n"
  133. if $self->layer_height <= 0;
  134. die "--layer-height must be a multiple of print resolution\n"
  135. if $self->layer_height / &Slic3r::SCALING_FACTOR % 1 != 0;
  136. # --first-layer-height
  137. die "Invalid value for --first-layer-height\n"
  138. if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/;
  139. die "Invalid value for --first-layer-height\n"
  140. if $self->get_value('first_layer_height') <= 0;
  141. # --filament-diameter
  142. die "Invalid value for --filament-diameter\n"
  143. if grep $_ < 1, @{$self->filament_diameter};
  144. # --nozzle-diameter
  145. die "Invalid value for --nozzle-diameter\n"
  146. if grep $_ < 0, @{$self->nozzle_diameter};
  147. # --perimeters
  148. die "Invalid value for --perimeters\n"
  149. if $self->perimeters < 0;
  150. # --solid-layers
  151. die "Invalid value for --solid-layers\n" if defined $self->solid_layers && $self->solid_layers < 0;
  152. die "Invalid value for --top-solid-layers\n" if $self->top_solid_layers < 0;
  153. die "Invalid value for --bottom-solid-layers\n" if $self->bottom_solid_layers < 0;
  154. # --gcode-flavor
  155. die "Invalid value for --gcode-flavor\n"
  156. if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
  157. die "--use-firmware-retraction is only supported by Marlin, Smoothie, Repetier and Machinekit firmware\n"
  158. if $self->use_firmware_retraction && $self->gcode_flavor ne 'smoothie'
  159. && $self->gcode_flavor ne 'reprap'
  160. && $self->gcode_flavor ne 'machinekit'
  161. && $self->gcode_flavor ne 'repetier';
  162. die "--use-firmware-retraction is not compatible with --wipe\n"
  163. if $self->use_firmware_retraction && first {$_} @{$self->wipe};
  164. # --fill-pattern
  165. die "Invalid value for --fill-pattern\n"
  166. if !first { $_ eq $self->fill_pattern } @{$Options->{fill_pattern}{values}};
  167. # --external-fill-pattern
  168. die "Invalid value for --top-infill-pattern\n"
  169. if !first { $_ eq $self->top_infill_pattern } @{$Options->{top_infill_pattern}{values}};
  170. die "Invalid value for --bottom-infill-pattern\n"
  171. if !first { $_ eq $self->bottom_infill_pattern } @{$Options->{bottom_infill_pattern}{values}};
  172. # --fill-density
  173. die "The selected fill pattern is not supposed to work at 100% density\n"
  174. if $self->fill_density == 100
  175. && !first { $_ eq $self->fill_pattern } @{$Options->{external_fill_pattern}{values}};
  176. # --infill-every-layers
  177. die "Invalid value for --infill-every-layers\n"
  178. if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1;
  179. # --skirt-height
  180. die "Invalid value for --skirt-height\n"
  181. if $self->skirt_height < -1; # -1 means as tall as the object
  182. # --bridge-flow-ratio
  183. die "Invalid value for --bridge-flow-ratio\n"
  184. if $self->bridge_flow_ratio <= 0;
  185. # extruder clearance
  186. die "Invalid value for --extruder-clearance-radius\n"
  187. if $self->extruder_clearance_radius <= 0;
  188. die "Invalid value for --extruder-clearance-height\n"
  189. if $self->extruder_clearance_height <= 0;
  190. # --extrusion-multiplier
  191. die "Invalid value for --extrusion-multiplier\n"
  192. if defined first { $_ <= 0 } @{$self->extrusion_multiplier};
  193. # --default-acceleration
  194. die "Invalid zero value for --default-acceleration when using other acceleration settings\n"
  195. if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration)
  196. && !$self->default_acceleration;
  197. # --spiral-vase
  198. if ($self->spiral_vase) {
  199. # Note that we might want to have more than one perimeter on the bottom
  200. # solid layers.
  201. die "Can't make more than one perimeter when spiral vase mode is enabled\n"
  202. if $self->perimeters > 1;
  203. die "Can't make less than one perimeter when spiral vase mode is enabled\n"
  204. if $self->perimeters < 1;
  205. die "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0\n"
  206. if $self->fill_density > 0;
  207. die "Spiral vase mode is not compatible with top solid layers\n"
  208. if $self->top_solid_layers > 0;
  209. die "Spiral vase mode is not compatible with support material\n"
  210. if $self->support_material || $self->support_material_enforce_layers > 0;
  211. }
  212. # extrusion widths
  213. {
  214. my $max_nozzle_diameter = max(@{ $self->nozzle_diameter });
  215. die "Invalid extrusion width (too large)\n"
  216. if defined first { $_ > 10 * $max_nozzle_diameter }
  217. map $self->get_abs_value_over("${_}_extrusion_width", $max_nozzle_diameter),
  218. qw(perimeter infill solid_infill top_infill support_material first_layer);
  219. }
  220. # general validation, quick and dirty
  221. foreach my $opt_key (@{$self->get_keys}) {
  222. my $opt = $Options->{$opt_key};
  223. next unless defined $self->$opt_key;
  224. next unless defined $opt->{cli} && $opt->{cli} =~ /=(.+)$/;
  225. my $type = $1;
  226. my @values = ();
  227. if ($type =~ s/\@$//) {
  228. die "Invalid value for $opt_key\n" if ref($self->$opt_key) ne 'ARRAY';
  229. @values = @{ $self->$opt_key };
  230. } else {
  231. @values = ($self->$opt_key);
  232. }
  233. foreach my $value (@values) {
  234. if ($type eq 'i' || $type eq 'f' || $opt->{type} eq 'percent') {
  235. $value =~ s/%$// if $opt->{type} eq 'percent';
  236. die "Invalid value for $opt_key\n"
  237. if ($type eq 'i' && $value !~ /^-?\d+$/)
  238. || (($type eq 'f' || $opt->{type} eq 'percent') && $value !~ /^-?(?:\d+|\d*\.\d+)$/)
  239. || (defined $opt->{min} && $value < $opt->{min})
  240. || (defined $opt->{max} && $value > $opt->{max});
  241. } elsif ($type eq 's' && $opt->{type} eq 'select') {
  242. die "Invalid value for $opt_key\n"
  243. unless first { $_ eq $value } @{ $opt->{values} };
  244. }
  245. }
  246. }
  247. return 1;
  248. }
  249. # CLASS METHODS:
  250. # Write a "Windows" style ini file with categories enclosed in squre brackets.
  251. sub write_ini {
  252. my $class = shift;
  253. my ($file, $ini) = @_;
  254. Slic3r::open(\my $fh, '>', $file);
  255. binmode $fh, ':utf8';
  256. my $localtime = localtime;
  257. printf $fh "# generated by Slic3r $Slic3r::VERSION on %s\n", "$localtime";
  258. # make sure the _ category is the first one written
  259. foreach my $category (sort { ($a eq '_') ? -1 : ($a cmp $b) } keys %$ini) {
  260. printf $fh "\n[%s]\n", $category if $category ne '_';
  261. foreach my $key (sort keys %{$ini->{$category}}) {
  262. printf $fh "%s = %s\n", $key, $ini->{$category}{$key};
  263. }
  264. }
  265. close $fh;
  266. }
  267. # Parse a "Windows" style ini file with categories enclosed in squre brackets.
  268. # Returns a hash of hashes over strings.
  269. # {category}{name}=value
  270. # Non-categorized entries are stored under a category '_'.
  271. sub read_ini {
  272. my $class = shift;
  273. my ($file) = @_;
  274. local $/ = "\n";
  275. Slic3r::open(\my $fh, '<', $file)
  276. or die "Unable to open $file: $!\n";
  277. binmode $fh, ':utf8';
  278. my $ini = { _ => {} };
  279. my $category = '_';
  280. while (<$fh>) {
  281. s/\R+$//;
  282. next if /^\s+/;
  283. next if /^$/;
  284. next if /^\s*#/;
  285. if (/^\[(.+?)\]$/) {
  286. $category = $1;
  287. next;
  288. }
  289. /^(\w+) *= *(.*)/ or die "Unreadable configuration file (invalid data at line $.)\n";
  290. $ini->{$category}{$1} = $2;
  291. }
  292. close $fh;
  293. return $ini;
  294. }
  295. package Slic3r::Config::Static;
  296. use parent 'Slic3r::Config';
  297. sub Slic3r::Config::GCode::new { Slic3r::Config::Static::new_GCodeConfig }
  298. sub Slic3r::Config::Print::new { Slic3r::Config::Static::new_PrintConfig }
  299. sub Slic3r::Config::PrintObject::new { Slic3r::Config::Static::new_PrintObjectConfig }
  300. sub Slic3r::Config::PrintRegion::new { Slic3r::Config::Static::new_PrintRegionConfig }
  301. sub Slic3r::Config::Full::new { Slic3r::Config::Static::new_FullPrintConfig }
  302. sub Slic3r::Config::SLAPrint::new { Slic3r::Config::Static::new_SLAPrintConfig }
  303. 1;