From 7e34244b05a16b9f0de27a2266e4196070063e3f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 27 Jul 2012 21:13:03 +0200 Subject: [PATCH] Refactored configuration handling. Slic3r::Config is now an object. Multiple partial config objects are used throughout the codebase as local repositories, then merged and serialized when necessary. --- lib/Slic3r.pm | 152 +------- lib/Slic3r/Config.pm | 677 ++++++++++++++++++--------------- lib/Slic3r/ExPolygon.pm | 2 +- lib/Slic3r/Extruder.pm | 6 +- lib/Slic3r/ExtrusionPath.pm | 2 +- lib/Slic3r/Fill.pm | 12 +- lib/Slic3r/Fill/Base.pm | 2 +- lib/Slic3r/Flow.pm | 4 +- lib/Slic3r/GCode.pm | 120 +++--- lib/Slic3r/GUI.pm | 11 +- lib/Slic3r/GUI/ConfigWizard.pm | 8 +- lib/Slic3r/GUI/OptionsGroup.pm | 46 ++- lib/Slic3r/GUI/Plater.pm | 56 ++- lib/Slic3r/GUI/SkeinPanel.pm | 52 ++- lib/Slic3r/GUI/Tab.pm | 114 ++++-- lib/Slic3r/Geometry.pm | 8 +- lib/Slic3r/Layer.pm | 54 +-- lib/Slic3r/Polygon.pm | 2 +- lib/Slic3r/Print.pm | 196 ++++++---- lib/Slic3r/Print/Object.pm | 40 +- lib/Slic3r/SVG.pm | 4 +- lib/Slic3r/TriangleMesh.pm | 4 +- slic3r.pl | 179 +++++---- 23 files changed, 918 insertions(+), 833 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 957e30078b..5d07dc826c 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -50,153 +50,27 @@ use Slic3r::Surface; use Slic3r::TriangleMesh; eval "use Slic3r::Build"; -our $threads = $have_threads ? 2 : 1; +use constant SCALING_FACTOR => 0.000001; +use constant RESOLUTION => 0.01; +use constant OVERLAP_FACTOR => 0.5; +use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI; -# miscellaneous options -our $notes = ''; - -# output options -our $output_filename_format = '[input_filename_base].gcode'; -our $post_process = []; - -# printer options -our $print_center = [100,100]; # object will be centered around this point -our $z_offset = 0; -our $gcode_flavor = 'reprap'; -our $use_relative_e_distances = 0; -our $extrusion_axis = 'E'; -our $gcode_arcs = 0; -our $g0 = 0; -our $gcode_comments = 0; - -# filament options -our $filament_diameter = [3]; # mm -our $extrusion_multiplier = [1]; -our $temperature = [200]; -our $first_layer_temperature= [@$temperature]; -our $bed_temperature = 0; -our $first_layer_bed_temperature = $bed_temperature; - -# extruders -our $extruders = []; -our $nozzle_diameter = [0.5]; - -# extruder mapping (1-based indexes) -our $perimeter_extruder = 1; -our $infill_extruder = 1; -our $support_material_extruder = 1; - -# speed options -our $travel_speed = 130; # mm/s -our $perimeter_speed = 30; # mm/s -our $small_perimeter_speed = 30; # mm/s or % -our $external_perimeter_speed = '100%'; # mm/s or % -our $infill_speed = 60; # mm/s -our $solid_infill_speed = 60; # mm/s or % -our $top_solid_infill_speed = 50; # mm/s or % -our $bridge_speed = 60; # mm/s -our $first_layer_speed = '30%'; # mm/s or % - -# acceleration options -our $acceleration = 0; -our $perimeter_acceleration = 25; # mm/s^2 -our $infill_acceleration = 50; # mm/s^2 - -# accuracy options -our $scaling_factor = 0.000001; -our $resolution = 0.01; -our $small_perimeter_length = (6.5 / $scaling_factor)*2*PI; -our $layer_height = 0.4; -our $first_layer_height = '100%'; # mm or % -our $_first_layer_height = undef; # mm (computed) -our $infill_every_layers = 1; - -# flow options -our $extrusion_width = 0; -our $first_layer_extrusion_width = '200%'; -our $perimeter_extrusion_width = 0; -our $infill_extrusion_width = 0; -our $support_material_extrusion_width = 0; -our $bridge_flow_ratio = 1; -our $overlap_factor = 0.5; -our $flow; -our $first_layer_flow; -our $perimeters_flow; -our $infill_flow; -our $support_material_flow; - -# print options -our $perimeters = 3; -our $solid_layers = 3; -our $fill_pattern = 'rectilinear'; -our $solid_fill_pattern = 'rectilinear'; -our $fill_density = 0.4; # 1 = 100% -our $fill_angle = 45; -our $extra_perimeters = 1; -our $randomize_start = 1; -our $support_material = 0; -our $support_material_threshold = 45; -our $support_material_pattern = 'rectilinear'; -our $support_material_spacing = 2.5; -our $support_material_angle = 0; -our $start_gcode = "G28 ; home all axes"; -our $end_gcode = <<"END"; -M104 S0 ; turn off temperature -G28 X0 ; home X axis -M84 ; disable motors -END -our $layer_gcode = ''; - -# retraction options -our $retract_length = 1; # mm -our $retract_restart_extra = 0; # mm -our $retract_speed = 30; # mm/s -our $retract_before_travel = 2; # mm -our $retract_lift = 0; # mm - -# cooling options -our $cooling = 0; -our $min_fan_speed = 35; -our $max_fan_speed = 100; -our $bridge_fan_speed = 100; -our $fan_below_layer_time = 60; -our $slowdown_below_layer_time = 15; -our $min_print_speed = 10; -our $disable_fan_first_layers = 1; -our $fan_always_on = 0; - -# skirt/brim options -our $skirts = 1; -our $skirt_distance = 6; # mm -our $skirt_height = 1; # layers -our $brim_width = 0; # mm - -# transform options -our $scale = 1; -our $rotate = 0; -our $duplicate_mode = 'no'; -our $duplicate = 1; -our $bed_size = [200,200]; -our $duplicate_grid = [1,1]; -our $duplicate_distance = 6; # mm - -# sequential printing -our $complete_objects = 0; -our $extruder_clearance_radius = 20; # mm -our $extruder_clearance_height = 20; # mm - -our $Defaults = Slic3r::Config->current; -our $Settings = { presets => {} }; # application settings +# The following variables hold the objects used throughout the slicing +# process. They should belong to the Print object, but we are keeping +# them here because it makes accessing them slightly faster. +our $Config; +our $extruders; +our ($flow, $first_layer_flow, $perimeter_flow, $infill_flow, $support_material_flow); sub parallelize { my %params = @_; - if (!$params{disable} && $Slic3r::have_threads && $Slic3r::threads > 1) { + if (!$params{disable} && $Slic3r::have_threads && $Config->threads > 1) { my $q = Thread::Queue->new; - $q->enqueue(@{ $params{items} }, (map undef, 1..$Slic3r::threads)); + $q->enqueue(@{ $params{items} }, (map undef, 1..$Config->threads)); my $thread_cb = sub { $params{thread_cb}->($q) }; - foreach my $th (map threads->create($thread_cb), 1..$Slic3r::threads) { + foreach my $th (map threads->create($thread_cb), 1..$Config->threads) { $params{collect_cb}->($th->join); } } else { diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 8def706cf7..818665cd25 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -8,12 +8,6 @@ use constant PI => 4 * atan2(1, 1); # cemetery of old config settings our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool); -our %Groups = ( - print => [qw(layer_height first_layer_height perimeters randomize_start extra_perimeters solid_layers fill_density fill_angle fill_pattern solid_fill_pattern infill_every_layers perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed bridge_speed travel_speed first_layer_speed skirts skirt_distance skirt_height brim_width support_material support_material_threshold support_material_pattern support_material_pattern support_material_spacing support_material_angle notes complete_objects extruder_clearance_radius extruder_clearance_height gcode_comments output_filename_format post_process extrusion_width first_layer_extrusion_width infill_extrusion_width support_material_extrusion_width bridge_flow_ratio duplicate_distance)], - filament => [qw(filament_diameter extrusion_multiplier temperature first_layer_temperature bed_temperature first_layer_bed_temperature cooling min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers fan_always_on fan_below_layer_time slowdown_below_layer_time min_print_speed)], - printer => [qw(bed_size print_center z_offset gcode_flavor use_relative_e_distances nozzle_diameter retract_length retract_lift retract_speed retract_restart_extra retract_before_travel start_gcode end_gcode layer_gcode)], -); - our $Options = { # miscellaneous options @@ -27,6 +21,7 @@ our $Options = { height => 130, serialize => sub { join '\n', split /\R/, $_[0] }, deserialize => sub { join "\n", split /\\n/, $_[0] }, + default => '', }, 'threads' => { label => 'Threads', @@ -36,6 +31,8 @@ our $Options = { type => 'i', min => 1, max => 16, + default => $Slic3r::have_threads ? 2 : 1, + readonly => !$Slic3r::have_threads, }, # output options @@ -45,6 +42,7 @@ our $Options = { cli => 'output-filename-format=s', type => 's', full_width => 1, + default => '[input_filename_base].gcode', }, # printer options @@ -55,7 +53,8 @@ our $Options = { cli => 'print-center=s', type => 'point', serialize => sub { join ',', @{$_[0]} }, - deserialize => sub { [ split /,/, $_[0] ] }, + deserialize => sub { [ split /[,x]/, $_[0] ] }, + default => [100,100], }, 'gcode_flavor' => { label => 'G-code flavor', @@ -64,18 +63,21 @@ our $Options = { type => 'select', values => [qw(reprap teacup makerbot mach3 no-extrusion)], labels => ['RepRap (Marlin/Sprinter)', 'Teacup', 'MakerBot', 'Mach3/EMC', 'No extrusion'], + default => 'reprap', }, 'use_relative_e_distances' => { label => 'Use relative E distances', tooltip => 'If your firmware requires relative E values, check this, otherwise leave it unchecked. Most firmwares use absolute values.', cli => 'use-relative-e-distances!', type => 'bool', + default => 0, }, 'extrusion_axis' => { label => 'Extrusion axis', tooltip => 'Use this option to set the axis letter associated to your printer\'s extruder (usually E but some printers use A).', cli => 'extrusion-axis=s', type => 's', + default => 'E', }, 'z_offset' => { label => 'Z offset', @@ -83,24 +85,28 @@ our $Options = { sidetext => 'mm', cli => 'z-offset=f', type => 'f', + default => 0, }, 'gcode_arcs' => { label => 'Use native G-code arcs', tooltip => 'This experimental feature tries to detect arcs from segments and generates G2/G3 arc commands instead of multiple straight G1 commands.', cli => 'gcode-arcs!', type => 'bool', + default => 0, }, 'g0' => { label => 'Use G0 for travel moves', tooltip => 'Only enable this if your firmware supports G0 properly (thus decouples all axes using their maximum speeds instead of synchronizing them). Travel moves and retractions will be combined in single commands, speeding them print up.', cli => 'g0!', type => 'bool', + default => 0, }, 'gcode_comments' => { label => 'Verbose G-code', tooltip => 'Enable this to get a commented G-code file, with each line explained by a descriptive text. If you print from SD card, the additional weight of the file could make your firmware slow down.', cli => 'gcode-comments!', type => 'bool', + default => 0, }, # extruders options @@ -112,6 +118,7 @@ our $Options = { sidetext => 'mm', serialize => sub { join ',', @{$_[0]} }, deserialize => sub { [ split /,/, $_[0] ] }, + default => [0.5], }, 'filament_diameter' => { label => 'Diameter', @@ -121,6 +128,7 @@ our $Options = { type => 'f', serialize => sub { join ',', @{$_[0]} }, deserialize => sub { [ split /,/, $_[0] ] }, + default => [3], }, 'extrusion_multiplier' => { label => 'Extrusion multiplier', @@ -129,6 +137,7 @@ our $Options = { type => 'f', serialize => sub { join ',', @{$_[0]} }, deserialize => sub { [ split /,/, $_[0] ] }, + default => [1], }, 'temperature' => { label => 'Temperature', @@ -139,6 +148,7 @@ our $Options = { max => 300, serialize => sub { join ',', @{$_[0]} }, deserialize => sub { [ split /,/, $_[0] ] }, + default => [200], }, 'first_layer_temperature' => { label => 'First layer temperature', @@ -149,6 +159,7 @@ our $Options = { serialize => sub { join ',', @{$_[0]} }, deserialize => sub { [ split /,/, $_[0] ] }, max => 300, + default => [200], }, # extruder mapping @@ -157,16 +168,19 @@ our $Options = { cli => 'perimeter-extruder=i', type => 'i', aliases => [qw(perimeters_extruder)], + default => 1, }, 'infill_extruder' => { label => 'Infill extruder', cli => 'infill-extruder=i', type => 'i', + default => 1, }, 'support_material_extruder' => { label => 'Extruder', cli => 'support-material-extruder=i', type => 'i', + default => 1, }, # filament options @@ -177,6 +191,7 @@ our $Options = { cli => 'first-layer-bed-temperature=i', type => 'i', max => 300, + default => 0, }, 'bed_temperature' => { label => 'Bed Temperature', @@ -185,6 +200,7 @@ our $Options = { cli => 'bed-temperature=i', type => 'i', max => 300, + default => 0, }, # speed options @@ -195,6 +211,7 @@ our $Options = { cli => 'travel-speed=f', type => 'f', aliases => [qw(travel_feed_rate)], + default => 130, }, 'perimeter_speed' => { label => 'Perimeters', @@ -203,6 +220,7 @@ our $Options = { cli => 'perimeter-speed=f', type => 'f', aliases => [qw(perimeter_feed_rate)], + default => 30, }, 'small_perimeter_speed' => { label => 'Small perimeters', @@ -211,6 +229,7 @@ our $Options = { cli => 'small-perimeter-speed=s', type => 'f', ratio_over => 'perimeter_speed', + default => 30, }, 'external_perimeter_speed' => { label => 'External perimeters', @@ -219,6 +238,7 @@ our $Options = { cli => 'external-perimeter-speed=s', type => 'f', ratio_over => 'perimeter_speed', + default => '100%', }, 'infill_speed' => { label => 'Infill', @@ -227,6 +247,7 @@ our $Options = { cli => 'infill-speed=f', type => 'f', aliases => [qw(print_feed_rate infill_feed_rate)], + default => 60, }, 'solid_infill_speed' => { label => 'Solid infill', @@ -236,6 +257,7 @@ our $Options = { type => 'f', ratio_over => 'infill_speed', aliases => [qw(solid_infill_feed_rate)], + default => 60, }, 'top_solid_infill_speed' => { label => 'Top solid infill', @@ -244,6 +266,7 @@ our $Options = { cli => 'top-solid-infill-speed=s', type => 'f', ratio_over => 'solid_infill_speed', + default => 50, }, 'bridge_speed' => { label => 'Bridges', @@ -252,6 +275,7 @@ our $Options = { cli => 'bridge-speed=f', type => 'f', aliases => [qw(bridge_feed_rate)], + default => 60, }, 'first_layer_speed' => { label => 'First layer speed', @@ -259,6 +283,7 @@ our $Options = { sidetext => 'mm/s or %', cli => 'first-layer-speed=s', type => 'f', + default => '30%', }, # acceleration options @@ -266,18 +291,21 @@ our $Options = { label => 'Enable acceleration control', cli => 'acceleration!', type => 'bool', + default => 0, }, 'perimeter_acceleration' => { label => 'Perimeters', sidetext => 'mm/s²', cli => 'perimeter-acceleration', type => 'f', + default => 25, }, 'infill_acceleration' => { label => 'Infill', sidetext => 'mm/s²', cli => 'infill-acceleration', type => 'f', + default => 50, }, # accuracy options @@ -287,6 +315,7 @@ our $Options = { sidetext => 'mm', cli => 'layer-height=f', type => 'f', + default => 0.4, }, 'first_layer_height' => { label => 'First layer height', @@ -295,6 +324,7 @@ our $Options = { cli => 'first-layer-height=s', type => 'f', ratio_over => 'layer_height', + default => '100%', }, 'infill_every_layers' => { label => 'Infill every', @@ -303,6 +333,7 @@ our $Options = { cli => 'infill-every-layers=i', type => 'i', min => 1, + default => 1, }, # flow options @@ -312,6 +343,7 @@ our $Options = { sidetext => 'mm or % (leave 0 for auto)', cli => 'extrusion-width=s', type => 'f', + default => 0, }, 'first_layer_extrusion_width' => { label => 'First layer', @@ -319,6 +351,7 @@ our $Options = { sidetext => 'mm or % (leave 0 for default)', cli => 'first-layer-extrusion-width=s', type => 'f', + default => '200%', }, 'perimeter_extrusion_width' => { label => 'Perimeters', @@ -327,6 +360,7 @@ our $Options = { cli => 'perimeter-extrusion-width=s', type => 'f', aliases => [qw(perimeters_extrusion_width)], + default => 0, }, 'infill_extrusion_width' => { label => 'Infill', @@ -334,6 +368,7 @@ our $Options = { sidetext => 'mm or % (leave 0 for default)', cli => 'infill-extrusion-width=s', type => 'f', + default => 0, }, 'support_material_extrusion_width' => { label => 'Support material', @@ -341,12 +376,14 @@ our $Options = { sidetext => 'mm or % (leave 0 for default)', cli => 'support-material-extrusion-width=s', type => 'f', + default => 0, }, 'bridge_flow_ratio' => { label => 'Bridge flow ratio', tooltip => 'This factor affects the amount of plastic for bridging. You can decrease it slightly to pull the extrudates and prevent sagging, although default settings are usually good and you should experiment with cooling (use a fan) before tweaking this.', cli => 'bridge-flow-ratio=f', type => 'f', + default => 1, }, # print options @@ -356,12 +393,14 @@ our $Options = { cli => 'perimeters=i', type => 'i', aliases => [qw(perimeter_offsets)], + default => 3, }, 'solid_layers' => { label => 'Solid layers', tooltip => 'Number of solid layers to generate on top and bottom.', cli => 'solid-layers=i', type => 'i', + default => 3, }, 'fill_pattern' => { label => 'Fill pattern', @@ -370,6 +409,7 @@ our $Options = { type => 'select', values => [qw(rectilinear line concentric honeycomb hilbertcurve archimedeanchords octagramspiral)], labels => [qw(rectilinear line concentric honeycomb), 'hilbertcurve (slow)', 'archimedeanchords (slow)', 'octagramspiral (slow)'], + default => 'rectilinear', }, 'solid_fill_pattern' => { label => 'Top/bottom fill pattern', @@ -378,12 +418,14 @@ our $Options = { type => 'select', values => [qw(rectilinear concentric hilbertcurve archimedeanchords octagramspiral)], labels => [qw(rectilinear concentric), 'hilbertcurve (slow)', 'archimedeanchords (slow)', 'octagramspiral (slow)'], + default => 'rectilinear', }, 'fill_density' => { label => 'Fill density', tooltip => 'Density of internal infill, expressed in the range 0 - 1.', cli => 'fill-density=f', type => 'f', + default => 0.4, }, 'fill_angle' => { label => 'Fill angle', @@ -392,23 +434,27 @@ our $Options = { cli => 'fill-angle=i', type => 'i', max => 359, + default => 45, }, 'extra_perimeters' => { label => 'Generate extra perimeters when needed', cli => 'extra-perimeters!', type => 'bool', + default => 1, }, 'randomize_start' => { label => 'Randomize starting points', tooltip => 'Start each layer from a different vertex to prevent plastic build-up on the same corner.', cli => 'randomize-start!', type => 'bool', + default => 1, }, 'support_material' => { label => 'Generate support material', tooltip => 'Enable support material generation.', cli => 'support-material!', type => 'bool', + default => 0, }, 'support_material_threshold' => { label => 'Overhang threshold', @@ -416,6 +462,7 @@ our $Options = { sidetext => '°', cli => 'support-material-threshold=i', type => 'i', + default => 45, }, 'support_material_pattern' => { label => 'Pattern', @@ -424,6 +471,7 @@ our $Options = { type => 'select', values => [qw(rectilinear honeycomb)], labels => [qw(rectilinear honeycomb)], + default => 'rectilinear', }, 'support_material_spacing' => { label => 'Pattern spacing', @@ -431,6 +479,7 @@ our $Options = { sidetext => 'mm', cli => 'support-material-spacing=f', type => 'f', + default => 2.5, }, 'support_material_angle' => { label => 'Pattern angle', @@ -438,6 +487,7 @@ our $Options = { sidetext => '°', cli => 'support-material-angle=i', type => 'i', + default => 0, }, 'start_gcode' => { label => 'Start G-code', @@ -449,6 +499,7 @@ our $Options = { height => 120, serialize => sub { join '\n', split /\R+/, $_[0] }, deserialize => sub { join "\n", split /\\n/, $_[0] }, + default => 'G28 ; home all axes', }, 'end_gcode' => { label => 'End G-code', @@ -460,6 +511,11 @@ our $Options = { height => 120, serialize => sub { join '\n', split /\R+/, $_[0] }, deserialize => sub { join "\n", split /\\n/, $_[0] }, + default => <<'END', +M104 S0 ; turn off temperature +G28 X0 ; home X axis +M84 ; disable motors +END }, 'layer_gcode' => { label => 'Layer change G-code', @@ -471,6 +527,7 @@ our $Options = { height => 50, serialize => sub { join '\n', split /\R+/, $_[0] }, deserialize => sub { join "\n", split /\\n/, $_[0] }, + default => '', }, 'post_process' => { label => 'Post-processing scripts', @@ -482,6 +539,7 @@ our $Options = { height => 60, serialize => sub { join '; ', @{$_[0]} }, deserialize => sub { [ split /\s*;\s*/, $_[0] ] }, + default => [], }, # retraction options @@ -491,6 +549,7 @@ our $Options = { sidetext => 'mm (zero to disable)', cli => 'retract-length=f', type => 'f', + default => 1, }, 'retract_speed' => { label => 'Speed', @@ -499,6 +558,7 @@ our $Options = { cli => 'retract-speed=f', type => 'i', max => 1000, + default => 30, }, 'retract_restart_extra' => { label => 'Extra length on restart', @@ -506,6 +566,7 @@ our $Options = { sidetext => 'mm', cli => 'retract-restart-extra=f', type => 'f', + default => 0, }, 'retract_before_travel' => { label => 'Minimum travel after retraction', @@ -513,6 +574,7 @@ our $Options = { sidetext => 'mm', cli => 'retract-before-travel=f', type => 'f', + default => 2, }, 'retract_lift' => { label => 'Lift Z', @@ -520,6 +582,7 @@ our $Options = { sidetext => 'mm', cli => 'retract-lift=f', type => 'f', + default => 0, }, # cooling options @@ -528,6 +591,7 @@ our $Options = { tooltip => 'This flag enables all the cooling features.', cli => 'cooling!', type => 'bool', + default => 0, }, 'min_fan_speed' => { label => 'Min fan speed', @@ -536,6 +600,7 @@ our $Options = { cli => 'min-fan-speed=i', type => 'i', max => 1000, + default => 35, }, 'max_fan_speed' => { label => 'Max fan speed', @@ -544,6 +609,7 @@ our $Options = { cli => 'max-fan-speed=i', type => 'i', max => 1000, + default => 100, }, 'bridge_fan_speed' => { label => 'Bridge fan speed', @@ -552,6 +618,7 @@ our $Options = { cli => 'bridge-fan-speed=i', type => 'i', max => 1000, + default => 100, }, 'fan_below_layer_time' => { label => 'Enable fan if layer print time is below', @@ -561,6 +628,7 @@ our $Options = { type => 'i', max => 1000, width => 60, + default => 60, }, 'slowdown_below_layer_time' => { label => 'Slow down if layer print time is below', @@ -570,6 +638,7 @@ our $Options = { type => 'i', max => 1000, width => 60, + default => 15, }, 'min_print_speed' => { label => 'Min print speed', @@ -578,6 +647,7 @@ our $Options = { cli => 'min-print-speed=f', type => 'i', max => 1000, + default => 10, }, 'disable_fan_first_layers' => { label => 'Disable fan for the first', @@ -586,12 +656,14 @@ our $Options = { cli => 'disable-fan-first-layers=i', type => 'i', max => 1000, + default => 1, }, 'fan_always_on' => { label => 'Keep fan always on', tooltip => 'If this is enabled, fan will never be disabled and will be kept running at least at its minimum speed. Useful for PLA, harmful for ABS.', cli => 'fan-always-on!', type => 'bool', + default => 0, }, # skirt/brim options @@ -600,6 +672,7 @@ our $Options = { tooltip => 'Number of loops for this skirt, in other words its thickness. Set this to zero to disable skirt.', cli => 'skirts=i', type => 'i', + default => 1, }, 'skirt_distance' => { label => 'Distance from object', @@ -607,6 +680,7 @@ our $Options = { sidetext => 'mm', cli => 'skirt-distance=f', type => 'f', + default => 6, }, 'skirt_height' => { label => 'Skirt height', @@ -614,6 +688,7 @@ our $Options = { sidetext => 'layers', cli => 'skirt-height=i', type => 'i', + default => 1, }, 'brim_width' => { label => 'Brim width', @@ -621,6 +696,7 @@ our $Options = { sidetext => 'mm', cli => 'brim-width=f', type => 'f', + default => 0, }, # transform options @@ -628,6 +704,7 @@ our $Options = { label => 'Scale', cli => 'scale=f', type => 'f', + default => 1, }, 'rotate' => { label => 'Rotate', @@ -635,12 +712,14 @@ our $Options = { cli => 'rotate=i', type => 'i', max => 359, + default => 0, }, 'duplicate' => { - label => 'Copies (autoarrange)', - cli => 'duplicate=i', + label => 'Copies (autoarrange)', + cli => 'duplicate=i', type => 'i', min => 1, + default => 1, }, 'bed_size' => { label => 'Bed size', @@ -649,14 +728,16 @@ our $Options = { cli => 'bed-size=s', type => 'point', serialize => sub { join ',', @{$_[0]} }, - deserialize => sub { [ split /,/, $_[0] ] }, + deserialize => sub { [ split /[,x]/, $_[0] ] }, + default => [200,200], }, 'duplicate_grid' => { label => 'Copies (grid)', cli => 'duplicate-grid=s', type => 'point', serialize => sub { join ',', @{$_[0]} }, - deserialize => sub { [ split /,/, $_[0] ] }, + deserialize => sub { [ split /[,x]/, $_[0] ] }, + default => [1,1], }, 'duplicate_distance' => { label => 'Distance between copies', @@ -665,6 +746,7 @@ our $Options = { cli => 'duplicate-distance=f', type => 'f', aliases => [qw(multiply_distance)], + default => 6, }, # sequential printing options @@ -673,6 +755,7 @@ our $Options = { tooltip => 'When printing multiple objects or copies, this feature will complete each object before moving onto next one (and starting it from its bottom layer). This feature is useful to avoid the risk of ruined prints. Slic3r should warn and prevent you from extruder collisions, but beware.', cli => 'complete-objects!', type => 'bool', + default => 0, }, 'extruder_clearance_radius' => { label => 'Extruder clearance radius', @@ -680,6 +763,7 @@ our $Options = { sidetext => 'mm', cli => 'extruder-clearance-radius=f', type => 'f', + default => 20, }, 'extruder_clearance_height' => { label => 'Extruder clearance height', @@ -687,50 +771,302 @@ our $Options = { sidetext => 'mm', cli => 'extruder-clearance-height=f', type => 'f', + default => 20, }, }; -sub get { - my $class = @_ == 2 ? shift : undef; - my ($opt_key) = @_; +# generate accessors +{ no strict 'refs'; - my $value = ${"Slic3r::$opt_key"}; - $value = get($Options->{$opt_key}{ratio_over}) * $1/100 + for my $opt_key (keys %$Options) { + *{$opt_key} = sub { $_[0]{$opt_key} }; + } +} + +sub new { + my $class = shift; + my %args = @_; + + my $self = bless {}, $class; + $self->apply(%args); + return $self; +} + +sub new_from_defaults { + my $class = shift; + my @opt_keys = + return $class->new(map { $_ => $Options->{$_}{default} } (@_ ? @_ : keys %$Options)); +} + +sub new_from_cli { + my $class = shift; + my %args = @_; + + delete $args{$_} for grep !defined $args{$_}, keys %args; + + for (qw(start end layer)) { + my $opt_key = "${_}_gcode"; + if ($args{$opt_key}) { + die "Invalid value for --${_}-gcode: file does not exist\n" + if !-e $args{$opt_key}; + open my $fh, "<", $args{$opt_key} + or die "Failed to open $args{$opt_key}\n"; + binmode $fh, ':utf8'; + $args{$opt_key} = do { local $/; <$fh> }; + close $fh; + } + } + + $args{$_} = $Options->{$_}{deserialize}->($args{$_}) + for grep exists $args{$_}, qw(print_center bed_size duplicate_grid); + + return $class->new(%args); +} + +sub merge { + my $class = shift; + return $class->new(map %$_, @_); +} + +sub load { + my $class = shift; + my ($file) = @_; + + my $ini = __PACKAGE__->read_ini($file); + my $config = __PACKAGE__->new; + $config->set($_, $ini->{_}{$_}, 1) for keys %{$ini->{_}}; + return $config; +} + +sub apply { + my $self = shift; + my %args = @_ == 1 ? %{$_[0]} : @_; # accept a single Config object too + + $self->set($_, $args{$_}) for keys %args; +} + +sub clone { + my $self = shift; + my $new = __PACKAGE__->new(%$self); + $new->{$_} = [@{$new->{$_}}] for grep { ref $new->{$_} eq 'ARRAY' } keys %$new; + return $new; +} + +sub get_value { + my $self = shift; + my ($opt_key) = @_; + + no strict 'refs'; + my $value = $self->get($opt_key); + $value = $self->get($Options->{$opt_key}{ratio_over}) * $1/100 if $Options->{$opt_key}{ratio_over} && $value =~ /^(\d+(?:\.\d+)?)%$/; return $value; } -sub get_raw { - my $class = @_ == 2 ? shift : undef; +sub get { + my $self = shift; my ($opt_key) = @_; - no strict 'refs'; - my $value = ${"Slic3r::$opt_key"}; - return $value; + + return $self->{$opt_key}; } sub set { - my $class = @_ == 3 ? shift : undef; - my ($opt_key, $value) = @_; - no strict 'refs'; - ${"Slic3r::$opt_key"} = $value; + my $self = shift; + my ($opt_key, $value, $deserialize) = @_; + + # handle legacy options + return if $opt_key ~~ @Ignore; + if ($opt_key =~ /^(extrusion_width|bottom_layer_speed|first_layer_height)_ratio$/) { + $opt_key = $1; + $opt_key =~ s/^bottom_layer_speed$/first_layer_speed/; + $value = $value =~ /^\d+(?:\.\d+)?$/ && $value != 0 ? ($value*100) . "%" : 0; + } + + if (!exists $Options->{$opt_key}) { + $opt_key = +(grep { $Options->{$_}{aliases} && grep $_ eq $opt_key, @{$Options->{$_}{aliases}} } keys %$Options)[0] + or warn "Unknown option $opt_key\n"; + } + + # clone arrayrefs + $value = [@$value] if ref $value eq 'ARRAY'; + + # deserialize if requested + $value = $Options->{$opt_key}{deserialize}->($value) + if $deserialize && $Options->{$opt_key}{deserialize}; + + $self->{$opt_key} = $value; +} + +sub set_ifndef { + my $self = shift; + my ($opt_key, $value, $deserialize) = @_; + + $self->set($opt_key, $value, $deserialize) + if !defined $self->get($opt_key); } sub serialize { - my $class = @_ == 2 ? shift : undef; + my $self = shift; my ($opt_key) = @_; - return $Options->{$opt_key}{serialize} - ? $Options->{$opt_key}{serialize}->(get_raw($opt_key)) - : get_raw($opt_key); + + my $value = $self->get($opt_key); + $value = $Options->{$opt_key}{serialize}->($value) if $Options->{$opt_key}{serialize}; + return $value; } -sub deserialize { - my $class = @_ == 3 ? shift : undef; - my ($opt_key, $value) = @_; - return $Options->{$opt_key}{deserialize} - ? set($opt_key, $Options->{$opt_key}{deserialize}->($value)) - : set($opt_key, $value); +sub save { + my $self = shift; + my ($file) = @_; + + my $ini = { _ => {} }; + foreach my $opt_key (sort keys %$self) { + next if $Options->{$opt_key}{gui_only}; + $ini->{_}{$opt_key} = $self->serialize($opt_key); + } + __PACKAGE__->write_ini($file, $ini); } +sub setenv { + my $self = shift; + + foreach my $opt_key (sort keys %$Options) { + next if $Options->{$opt_key}{gui_only}; + $ENV{"SLIC3R_" . uc $opt_key} = $self->serialize($opt_key); + } +} + +# this method is idempotent by design +sub validate { + my $self = shift; + + # -j, --threads + die "Invalid value for --threads\n" + if $self->threads < 1; + die "Your perl wasn't built with multithread support\n" + if $self->threads > 1 && !$self->have_threads; + + # --layer-height + die "Invalid value for --layer-height\n" + if $self->layer_height <= 0; + die "--layer-height must be a multiple of print resolution\n" + if $self->layer_height / &Slic3r::SCALING_FACTOR % 1 != 0; + + # --first-layer-height + die "Invalid value for --first-layer-height\n" + if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/; + + # --filament-diameter + die "Invalid value for --filament-diameter\n" + if grep $_ < 1, @{$self->filament_diameter}; + + # --nozzle-diameter + die "Invalid value for --nozzle-diameter\n" + if grep $_ < 0, @{$self->nozzle_diameter}; + die "--layer-height can't be greater than --nozzle-diameter\n" + if grep $self->layer_height > $_, @{$self->nozzle_diameter}; + die "First layer height can't be greater than --nozzle-diameter\n" + if grep $self->get_value('first_layer_height') > $_, @{$self->nozzle_diameter}; + + # --perimeters + die "Invalid value for --perimeters\n" + if $self->perimeters < 0; + + # --solid-layers + die "Invalid value for --solid-layers\n" + if $self->solid_layers < 0; + + # --print-center + die "Invalid value for --print-center\n" + if !ref $self->print_center + && (!$self->print_center || $self->print_center !~ /^\d+,\d+$/); + + # --fill-pattern + die "Invalid value for --fill-pattern\n" + if !exists $Slic3r::Fill::FillTypes{$self->fill_pattern}; + + # --solid-fill-pattern + die "Invalid value for --solid-fill-pattern\n" + if !exists $Slic3r::Fill::FillTypes{$self->solid_fill_pattern}; + + # --fill-density + die "Invalid value for --fill-density\n" + if $self->fill_density < 0 || $self->fill_density > 1; + + # --infill-every-layers + die "Invalid value for --infill-every-layers\n" + if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1; + # TODO: this check should be limited to the extruder used for infill + die "Maximum infill thickness can't exceed nozzle diameter\n" + if grep $self->infill_every_layers * $self->layer_height > $_, @{$self->nozzle_diameter}; + + # --scale + die "Invalid value for --scale\n" + if $self->scale <= 0; + + # --bed-size + die "Invalid value for --bed-size\n" + if !ref $self->bed_size + && (!$self->bed_size || $self->bed_size !~ /^\d+,\d+$/); + + # --duplicate-grid + die "Invalid value for --duplicate-grid\n" + if !ref $self->duplicate_grid + && (!$self->duplicate_grid || $self->duplicate_grid !~ /^\d+,\d+$/); + + # --duplicate + die "Invalid value for --duplicate or --duplicate-grid\n" + if !$self->duplicate || $self->duplicate < 1 || !$self->duplicate_grid + || (grep !$_, @{$self->duplicate_grid}); + die "Use either --duplicate or --duplicate-grid (using both doesn't make sense)\n" + if $self->duplicate > 1 && $self->duplicate_grid && (grep $_ && $_ > 1, @{$self->duplicate_grid}); + + # --skirt-height + die "Invalid value for --skirt-height\n" + if $self->skirt_height < 0; + + # --bridge-flow-ratio + die "Invalid value for --bridge-flow-ratio\n" + if $self->bridge_flow_ratio <= 0; + + # extruder clearance + die "Invalid value for --extruder-clearance-radius\n" + if $self->extruder_clearance_radius <= 0; + die "Invalid value for --extruder-clearance-height\n" + if $self->extruder_clearance_height <= 0; +} + +sub replace_options { + my $self = shift; + my ($string, $more_variables) = @_; + + if ($more_variables) { + my $variables = join '|', keys %$more_variables; + $string =~ s/\[($variables)\]/$more_variables->{$1}/eg; + } + + my @lt = localtime; $lt[5] += 1900; $lt[4] += 1; + $string =~ s/\[timestamp\]/sprintf '%04d%02d%02d-%02d%02d%02d', @lt[5,4,3,2,1,0]/egx; + $string =~ s/\[year\]/$lt[5]/eg; + $string =~ s/\[month\]/$lt[4]/eg; + $string =~ s/\[day\]/$lt[3]/eg; + $string =~ s/\[hour\]/$lt[2]/eg; + $string =~ s/\[minute\]/$lt[1]/eg; + $string =~ s/\[second\]/$lt[0]/eg; + $string =~ s/\[version\]/$Slic3r::VERSION/eg; + + # build a regexp to match the available options + my $options = join '|', + grep !$Slic3r::Config::Options->{$_}{multiline}, + grep exists $self->{$_}, + keys %{$Slic3r::Config::Options}; + + # use that regexp to search and replace option names with option values + $string =~ s/\[($options)\]/$self->serialize($1)/eg; + return $string; +} + +# CLASS METHODS: + sub write_ini { my $class = shift; my ($file, $ini) = @_; @@ -738,7 +1074,7 @@ sub write_ini { open my $fh, '>', $file; binmode $fh, ':utf8'; my $localtime = localtime; - printf $fh "# generated by Slic3r $Slic3r::VERSION on $localtime\n"; + printf $fh "# generated by Slic3r $Slic3r::VERSION on %s\n", "$localtime"; foreach my $category (sort keys %$ini) { printf $fh "\n[%s]\n", $category if $category ne '_'; foreach my $key (sort keys %{$ini->{$category}}) { @@ -748,43 +1084,6 @@ sub write_ini { close $fh; } -sub save { - my $class = shift; - my ($file, $group) = @_; - - my $ini = { _ => {} }; - foreach my $opt (sort keys %$Options) { - next if defined $group && not ($opt ~~ @{$Groups{$group}}); - next if $Options->{$opt}{gui_only}; - my $value = get_raw($opt); - $value = $Options->{$opt}{serialize}->($value) if $Options->{$opt}{serialize}; - $ini->{_}{$opt} = $value; - } - $class->write_ini($file, $ini); -} - -sub save_settings { - my $class = shift; - my ($file) = @_; - - $class->write_ini($file, $Slic3r::Settings); -} - -sub setenv { - my $class = shift; - foreach my $opt (sort keys %$Options) { - next if $Options->{$opt}{gui_only}; - my $value = get($opt); - $value = $Options->{$opt}{serialize}->($value) if $Options->{$opt}{serialize}; - $ENV{"SLIC3R_" . uc $opt} = $value; - } -} - -sub current { - my $class = shift; - return { map +($_ => get_raw($_)), sort keys %$Options }; -} - sub read_ini { my $class = shift; my ($file) = @_; @@ -812,236 +1111,4 @@ sub read_ini { return $ini; } -sub load_hash { - my $class = shift; - my ($hash, $group, $deserialized) = @_; - - my %ignore = map { $_ => 1 } @Ignore; - foreach my $key (sort keys %$hash) { - next if defined $group && not ($key ~~ @{$Groups{$group}}); - my $val = $hash->{$key}; - - # handle legacy options - next if $ignore{$key}; - if ($key =~ /^(extrusion_width|bottom_layer_speed|first_layer_height)_ratio$/) { - $key = $1; - $key =~ s/^bottom_layer_speed$/first_layer_speed/; - $val = $val =~ /^\d+(?:\.\d+)?$/ && $val != 0 ? ($val*100) . "%" : 0; - } - - if (!exists $Options->{$key}) { - $key = +(grep { $Options->{$_}{aliases} && grep $_ eq $key, @{$Options->{$_}{aliases}} } - keys %$Options)[0] or warn "Unknown option $key at line $.\n"; - } - next unless $key; - my $opt = $Options->{$key}; - set($key, ($opt->{deserialize} && !$deserialized) ? $opt->{deserialize}->($val) : $val); - } -} - -sub load { - my $class = shift; - my ($file) = @_; - - my $ini = __PACKAGE__->read_ini($file); - __PACKAGE__->load_hash($ini->{_}); -} - -sub validate_cli { - my $class = shift; - my ($opt) = @_; - - for (qw(start end layer)) { - if (defined $opt->{$_."_gcode"}) { - if ($opt->{$_."_gcode"} eq "") { - set($_."_gcode", ""); - } else { - die "Invalid value for --${_}-gcode: file does not exist" - if !-e $opt->{$_."_gcode"}; - open my $fh, "<", $opt->{$_."_gcode"}; - $opt->{$_."_gcode"} = do { local $/; <$fh> }; - close $fh; - } - } - } -} - -sub validate { - my $class = shift; - - # -j, --threads - die "Invalid value for --threads\n" - if $Slic3r::threads < 1; - die "Your perl wasn't built with multithread support\n" - if $Slic3r::threads > 1 && !$Slic3r::have_threads; - - # --layer-height - die "Invalid value for --layer-height\n" - if $Slic3r::layer_height <= 0; - die "--layer-height must be a multiple of print resolution\n" - if $Slic3r::layer_height / $Slic3r::scaling_factor % 1 != 0; - - # --first-layer-height - die "Invalid value for --first-layer-height\n" - if $Slic3r::first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/; - $Slic3r::_first_layer_height = Slic3r::Config->get('first_layer_height'); - - # --filament-diameter - die "Invalid value for --filament-diameter\n" - if grep $_ < 1, @$Slic3r::filament_diameter; - - # --nozzle-diameter - die "Invalid value for --nozzle-diameter\n" - if grep $_ < 0, @$Slic3r::nozzle_diameter; - die "--layer-height can't be greater than --nozzle-diameter\n" - if grep $Slic3r::layer_height > $_, @$Slic3r::nozzle_diameter; - die "First layer height can't be greater than --nozzle-diameter\n" - if grep $Slic3r::_first_layer_height > $_, @$Slic3r::nozzle_diameter; - - # initialize extruder(s) - $Slic3r::extruders = []; - for my $t (0, map $_-1, $Slic3r::perimeter_extruder, $Slic3r::infill_extruder, $Slic3r::support_material_extruder) { - $Slic3r::extruders->[$t] ||= Slic3r::Extruder->new( - map { $_ => Slic3r::Config->get($_)->[$t] // Slic3r::Config->get($_)->[0] } #/ - qw(nozzle_diameter filament_diameter extrusion_multiplier temperature first_layer_temperature) - ); - } - - # calculate flow - $Slic3r::flow = $Slic3r::extruders->[0]->make_flow(width => $Slic3r::extrusion_width); - if ($Slic3r::first_layer_extrusion_width) { - $Slic3r::first_layer_flow = $Slic3r::extruders->[0]->make_flow( - layer_height => $Slic3r::_first_layer_height, - width => $Slic3r::first_layer_extrusion_width, - ); - } - $Slic3r::perimeters_flow = $Slic3r::extruders->[ $Slic3r::perimeter_extruder-1 ] - ->make_flow(width => $Slic3r::perimeter_extrusion_width || $Slic3r::extrusion_width); - $Slic3r::infill_flow = $Slic3r::extruders->[ $Slic3r::infill_extruder-1 ] - ->make_flow(width => $Slic3r::infill_extrusion_width || $Slic3r::extrusion_width); - $Slic3r::support_material_flow = $Slic3r::extruders->[ $Slic3r::support_material_extruder-1 ] - ->make_flow(width => $Slic3r::support_material_extrusion_width || $Slic3r::extrusion_width); - - Slic3r::debugf "Default flow width = %s (spacing = %s)\n", - $Slic3r::flow->width, $Slic3r::flow->spacing; - - # --perimeters - die "Invalid value for --perimeters\n" - if $Slic3r::perimeters < 0; - - # --solid-layers - die "Invalid value for --solid-layers\n" - if $Slic3r::solid_layers < 0; - - # --print-center - die "Invalid value for --print-center\n" - if !ref $Slic3r::print_center - && (!$Slic3r::print_center || $Slic3r::print_center !~ /^\d+,\d+$/); - $Slic3r::print_center = [ split /[,x]/, $Slic3r::print_center ] - if !ref $Slic3r::print_center; - - # --fill-pattern - die "Invalid value for --fill-pattern\n" - if !exists $Slic3r::Fill::FillTypes{$Slic3r::fill_pattern}; - - # --solid-fill-pattern - die "Invalid value for --solid-fill-pattern\n" - if !exists $Slic3r::Fill::FillTypes{$Slic3r::solid_fill_pattern}; - - # --fill-density - die "Invalid value for --fill-density\n" - if $Slic3r::fill_density < 0 || $Slic3r::fill_density > 1; - - # --infill-every-layers - die "Invalid value for --infill-every-layers\n" - if $Slic3r::infill_every_layers !~ /^\d+$/ || $Slic3r::infill_every_layers < 1; - # TODO: this check should be limited to the extruder used for infill - die "Maximum infill thickness can't exceed nozzle diameter\n" - if grep $Slic3r::infill_every_layers * $Slic3r::layer_height > $_, @$Slic3r::nozzle_diameter; - - # --scale - die "Invalid value for --scale\n" - if $Slic3r::scale <= 0; - - # --bed-size - die "Invalid value for --bed-size\n" - if !ref $Slic3r::bed_size - && (!$Slic3r::bed_size || $Slic3r::bed_size !~ /^\d+,\d+$/); - $Slic3r::bed_size = [ split /[,x]/, $Slic3r::bed_size ] - if !ref $Slic3r::bed_size; - - # --duplicate-grid - die "Invalid value for --duplicate-grid\n" - if !ref $Slic3r::duplicate_grid - && (!$Slic3r::duplicate_grid || $Slic3r::duplicate_grid !~ /^\d+,\d+$/); - $Slic3r::duplicate_grid = [ split /[,x]/, $Slic3r::duplicate_grid ] - if !ref $Slic3r::duplicate_grid; - - # --duplicate - die "Invalid value for --duplicate or --duplicate-grid\n" - if !$Slic3r::duplicate || $Slic3r::duplicate < 1 || !$Slic3r::duplicate_grid - || (grep !$_, @$Slic3r::duplicate_grid); - die "Use either --duplicate or --duplicate-grid (using both doesn't make sense)\n" - if $Slic3r::duplicate > 1 && $Slic3r::duplicate_grid && (grep $_ && $_ > 1, @$Slic3r::duplicate_grid); - $Slic3r::duplicate_mode = 'autoarrange' if $Slic3r::duplicate > 1; - $Slic3r::duplicate_mode = 'grid' if grep $_ && $_ > 1, @$Slic3r::duplicate_grid; - - # --skirt-height - die "Invalid value for --skirt-height\n" - if $Slic3r::skirt_height < 0; - - # --bridge-flow-ratio - die "Invalid value for --bridge-flow-ratio\n" - if $Slic3r::bridge_flow_ratio <= 0; - - # extruder clearance - die "Invalid value for --extruder-clearance-radius\n" - if $Slic3r::extruder_clearance_radius <= 0; - die "Invalid value for --extruder-clearance-height\n" - if $Slic3r::extruder_clearance_height <= 0; - - $_->first_layer_temperature($_->temperature) for grep !defined $_->first_layer_temperature, @$Slic3r::extruders; - $Slic3r::first_layer_temperature->[$_] = $Slic3r::extruders->[$_]->first_layer_temperature for 0 .. $#$Slic3r::extruders; # this is needed to provide a value to the legacy GUI and for config file re-serialization - $Slic3r::first_layer_bed_temperature //= $Slic3r::bed_temperature; #/ - - # G-code flavors - $Slic3r::extrusion_axis = 'A' if $Slic3r::gcode_flavor eq 'mach3'; - $Slic3r::extrusion_axis = '' if $Slic3r::gcode_flavor eq 'no-extrusion'; - - # legacy with existing config files - $Slic3r::small_perimeter_speed ||= $Slic3r::perimeter_speed; - $Slic3r::bridge_speed ||= $Slic3r::infill_speed; - $Slic3r::solid_infill_speed ||= $Slic3r::infill_speed; - $Slic3r::top_solid_infill_speed ||= $Slic3r::solid_infill_speed; -} - -sub replace_options { - my $class = shift; - my ($string, $more_variables) = @_; - - if ($more_variables) { - my $variables = join '|', keys %$more_variables; - $string =~ s/\[($variables)\]/$more_variables->{$1}/eg; - } - - my @lt = localtime; $lt[5] += 1900; $lt[4] += 1; - $string =~ s/\[timestamp\]/sprintf '%04d%02d%02d-%02d%02d%02d', @lt[5,4,3,2,1,0]/egx; - $string =~ s/\[year\]/$lt[5]/eg; - $string =~ s/\[month\]/$lt[4]/eg; - $string =~ s/\[day\]/$lt[3]/eg; - $string =~ s/\[hour\]/$lt[2]/eg; - $string =~ s/\[minute\]/$lt[1]/eg; - $string =~ s/\[second\]/$lt[0]/eg; - $string =~ s/\[version\]/$Slic3r::VERSION/eg; - - # build a regexp to match the available options - my $options = join '|', - grep !$Slic3r::Config::Options->{$_}{multiline}, - keys %$Slic3r::Config::Options; - - # use that regexp to search and replace option names with option values - $string =~ s/\[($options)\]/Slic3r::Config->serialize($1)/eg; - return $string; -} - 1; diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index d39aa7291e..7037185519 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -62,7 +62,7 @@ sub boost_polygon { sub offset { my $self = shift; my ($distance, $scale, $joinType, $miterLimit) = @_; - $scale ||= $Slic3r::scaling_factor * 1000000; + $scale ||= &Slic3r::SCALING_FACTOR * 1000000; $joinType = JT_MITER if !defined $joinType; $miterLimit ||= 2; diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 07a0d07993..5f9ad418c2 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -29,15 +29,15 @@ sub mm3_per_mm { my $cache_key = "${s}_${h}"; if (!exists $self->_mm3_per_mm_cache->{$cache_key}) { my $w_threshold = $h + $self->nozzle_diameter; - my $s_threshold = $w_threshold - $Slic3r::overlap_factor * ($w_threshold - ($w_threshold - $h * (1 - PI/4))); + my $s_threshold = $w_threshold - &Slic3r::OVERLAP_FACTOR * ($w_threshold - ($w_threshold - $h * (1 - PI/4))); if ($s >= $s_threshold) { # rectangle with semicircles at the ends - my $w = $s + $Slic3r::overlap_factor * $h * (1 - PI/4); + my $w = $s + &Slic3r::OVERLAP_FACTOR * $h * (1 - PI/4); $self->_mm3_per_mm_cache->{$cache_key} = $w * $h + ($h**2) / 4 * (PI - 4); } else { # rectangle with shrunk semicircles at the ends - my $w = ($s + $self->nozzle_diameter * $Slic3r::overlap_factor * (PI/4 - 1)) / (1 + $Slic3r::overlap_factor * (PI/4 - 1)); + my $w = ($s + $self->nozzle_diameter * &Slic3r::OVERLAP_FACTOR * (PI/4 - 1)) / (1 + &Slic3r::OVERLAP_FACTOR * (PI/4 - 1)); $self->_mm3_per_mm_cache->{$cache_key} = $self->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4; } } diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index 63f2267e6b..a006d8712c 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -159,7 +159,7 @@ sub detect_arcs { my ($max_angle, $len_epsilon) = @_; $max_angle = deg2rad($max_angle || 15); - $len_epsilon ||= 10 / $Slic3r::scaling_factor; + $len_epsilon ||= 10 / &Slic3r::SCALING_FACTOR; my @points = @{$self->points}; my @paths = (); diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 184ecad1f9..152932da7a 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -39,7 +39,7 @@ sub BUILD { my $max_print_dimension = ($print_size->[X] > $print_size->[Y] ? $print_size->[X] : $print_size->[Y]) * sqrt(2); $self->max_print_dimension($max_print_dimension); - $self->filler($_) for ('rectilinear', $Slic3r::fill_pattern, $Slic3r::solid_fill_pattern); + $self->filler($_) for ('rectilinear', $Slic3r::Config->fill_pattern, $Slic3r::Config->solid_fill_pattern); } sub filler { @@ -131,8 +131,8 @@ sub make_fill { my @fills = (); my @fills_ordering_points = (); SURFACE: foreach my $surface (@surfaces) { - my $filler = $Slic3r::fill_pattern; - my $density = $Slic3r::fill_density; + my $filler = $Slic3r::Config->fill_pattern; + my $density = $Slic3r::Config->fill_density; my $flow_spacing = $layer->infill_flow->spacing; my $is_bridge = $layer->id > 0 && $surface->surface_type == S_TYPE_BOTTOM; my $is_solid = (grep { $surface->surface_type == $_ } S_TYPE_TOP, S_TYPE_BOTTOM, S_TYPE_INTERNALSOLID) ? 1 : 0; @@ -140,10 +140,10 @@ sub make_fill { # force 100% density and rectilinear fill for external surfaces if ($surface->surface_type != S_TYPE_INTERNAL) { $density = 1; - $filler = $Slic3r::solid_fill_pattern; + $filler = $Slic3r::Config->solid_fill_pattern; if ($is_bridge) { $filler = 'rectilinear'; - $flow_spacing = sqrt($Slic3r::bridge_flow_ratio * ($layer->infill_flow->nozzle_diameter**2)); + $flow_spacing = sqrt($Slic3r::Config->bridge_flow_ratio * ($layer->infill_flow->nozzle_diameter**2)); } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { $filler = 'rectilinear'; } @@ -181,7 +181,7 @@ sub make_fill { { my %args = ( role => EXTR_ROLE_SOLIDFILL, - flow_spacing => $layer->perimeters_flow->spacing, + flow_spacing => $layer->perimeter_flow->spacing, ); push @fills, map { $_->isa('Slic3r::Polygon') diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index a87ff804f3..dec8119915 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -5,7 +5,7 @@ use Moo; has 'print' => (is => 'rw'); has 'layer' => (is => 'rw'); has 'max_print_dimension' => (is => 'rw'); -has 'angle' => (is => 'rw', default => sub { $Slic3r::fill_angle }); +has 'angle' => (is => 'rw', default => sub { $Slic3r::Config->fill_angle }); use constant PI => 4 * atan2(1, 1); diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 959d6e3d36..9c01dd30c0 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -4,7 +4,7 @@ use Moo; use Slic3r::Geometry qw(PI); has 'nozzle_diameter' => (is => 'ro', required => 1); -has 'layer_height' => (is => 'ro', default => sub { $Slic3r::layer_height }); +has 'layer_height' => (is => 'ro', default => sub { $Slic3r::Config->layer_height }); has 'width' => (is => 'rwp', builder => 1); has 'spacing' => (is => 'lazy'); @@ -52,7 +52,7 @@ sub _build_spacing { # rectangle with shrunk semicircles at the ends $min_flow_spacing = $self->nozzle_diameter * (1 - PI/4) + $self->width * PI/4; } - return $self->width - $Slic3r::overlap_factor * ($self->width - $min_flow_spacing); + return $self->width - &Slic3r::OVERLAP_FACTOR * ($self->width - $min_flow_spacing); } 1; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 4270fdb2f3..f42f005d05 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -25,15 +25,15 @@ has 'dec' => (is => 'ro', default => sub { 3 } ); has 'speeds' => ( is => 'ro', default => sub {{ - travel => 60 * Slic3r::Config->get('travel_speed'), - perimeter => 60 * Slic3r::Config->get('perimeter_speed'), - small_perimeter => 60 * Slic3r::Config->get('small_perimeter_speed'), - external_perimeter => 60 * Slic3r::Config->get('external_perimeter_speed'), - infill => 60 * Slic3r::Config->get('infill_speed'), - solid_infill => 60 * Slic3r::Config->get('solid_infill_speed'), - top_solid_infill => 60 * Slic3r::Config->get('top_solid_infill_speed'), - bridge => 60 * Slic3r::Config->get('bridge_speed'), - retract => 60 * Slic3r::Config->get('retract_speed'), + travel => 60 * $Slic3r::Config->get_value('travel_speed'), + perimeter => 60 * $Slic3r::Config->get_value('perimeter_speed'), + small_perimeter => 60 * $Slic3r::Config->get_value('small_perimeter_speed'), + external_perimeter => 60 * $Slic3r::Config->get_value('external_perimeter_speed'), + infill => 60 * $Slic3r::Config->get_value('infill_speed'), + solid_infill => 60 * $Slic3r::Config->get_value('solid_infill_speed'), + top_solid_infill => 60 * $Slic3r::Config->get_value('top_solid_infill_speed'), + bridge => 60 * $Slic3r::Config->get_value('bridge_speed'), + retract => 60 * $Slic3r::Config->get_value('retract_speed'), }}, ); @@ -62,7 +62,7 @@ sub change_layer { my ($layer) = @_; $self->layer($layer); - my $z = $Slic3r::z_offset + $layer->print_z * $Slic3r::scaling_factor; + my $z = $Slic3r::Config->z_offset + $layer->print_z * &Slic3r::SCALING_FACTOR; my $gcode = ""; @@ -70,8 +70,8 @@ sub change_layer { $gcode .= $self->G0(undef, $z, 0, 'move to next layer (' . $layer->id . ')') if $self->z != $z && !$self->lifted; - $gcode .= Slic3r::Config->replace_options($Slic3r::layer_gcode) . "\n" - if $Slic3r::layer_gcode; + $gcode .= $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n" + if $Slic3r::Config->layer_gcode; return $gcode; } @@ -95,10 +95,10 @@ sub extrude_loop { # find the point of the loop that is closest to the current extruder position # or randomize if requested my $last_pos = $self->last_pos; - if ($Slic3r::randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) { + if ($Slic3r::Config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) { srand $self->layer->id * 10; - $last_pos = Slic3r::Point->new(scale $Slic3r::print_center->[X], scale $Slic3r::bed_size->[Y]); - $last_pos->rotate(rand(2*PI), $Slic3r::print_center); + $last_pos = Slic3r::Point->new(scale $Slic3r::Config->print_center->[X], scale $Slic3r::Config->bed_size->[Y]); + $last_pos->rotate(rand(2*PI), $Slic3r::Config->print_center); } my $start_index = $loop->nearest_point_index_to($last_pos); @@ -123,7 +123,7 @@ sub extrude_path { $path->merge_continuous_lines; # detect arcs - if ($Slic3r::gcode_arcs && !$recursive) { + if ($Slic3r::Config->gcode_arcs && !$recursive) { my $gcode = ""; foreach my $arc_path ($path->detect_arcs) { $gcode .= $self->extrude_path($arc_path, $description, 1); @@ -136,10 +136,10 @@ sub extrude_path { # retract if distance from previous position is greater or equal to the one # specified by the user *and* to the maximum distance between infill lines { - my $distance_from_last_pos = $self->last_pos->distance_to($path->points->[0]) * $Slic3r::scaling_factor; - my $distance_threshold = $Slic3r::retract_before_travel; - $distance_threshold = 2 * ($self->layer ? $self->layer->flow->width : $Slic3r::flow->width) / $Slic3r::fill_density * sqrt(2) - if 0 && $Slic3r::fill_density > 0 && $description =~ /fill/; + my $distance_from_last_pos = $self->last_pos->distance_to($path->points->[0]) * &Slic3r::SCALING_FACTOR; + my $distance_threshold = $Slic3r::Config->retract_before_travel; + $distance_threshold = 2 * ($self->layer ? $self->layer->flow->width : $Slic3r::flow->width) / $Slic3r::Config->fill_density * sqrt(2) + if 0 && $Slic3r::Config->fill_density > 0 && $description =~ /fill/; if ($distance_from_last_pos >= $distance_threshold) { $gcode .= $self->retract(travel_to => $path->points->[0]); @@ -181,12 +181,12 @@ sub extrude_path { } } - if ($Slic3r::cooling) { + if ($Slic3r::Config->cooling) { my $path_time = unscale($path_length) / $self->speeds->{$self->last_speed} * 60; if ($self->layer->id == 0) { - $path_time = $Slic3r::first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ + $path_time = $Slic3r::Config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ ? $path_time / ($1/100) - : unscale($path_length) / $Slic3r::first_layer_speed * 60; + : unscale($path_length) / $Slic3r::Config->first_layer_speed * 60; } $self->elapsed_time($self->elapsed_time + $path_time); } @@ -198,18 +198,18 @@ sub retract { my $self = shift; my %params = @_; - return "" unless $Slic3r::retract_length > 0 + return "" unless $Slic3r::Config->retract_length > 0 && !$self->retracted; # prepare moves $self->speed('retract'); - my $retract = [undef, undef, -$Slic3r::retract_length, "retract"]; - my $lift = ($Slic3r::retract_lift == 0 || defined $params{move_z}) + my $retract = [undef, undef, -$Slic3r::Config->retract_length, "retract"]; + my $lift = ($Slic3r::Config->retract_lift == 0 || defined $params{move_z}) ? undef - : [undef, $self->z + $Slic3r::retract_lift, 0, 'lift plate during retraction']; + : [undef, $self->z + $Slic3r::Config->retract_lift, 0, 'lift plate during retraction']; my $gcode = ""; - if (($Slic3r::g0 || $Slic3r::gcode_flavor eq 'mach3') && $params{travel_to}) { + if (($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3') && $params{travel_to}) { if ($lift) { # combine lift and retract $lift->[2] = $retract->[2]; @@ -219,14 +219,14 @@ sub retract { my $travel = [$params{travel_to}, undef, $retract->[2], 'travel and retract']; $gcode .= $self->G0(@$travel); } - } elsif (($Slic3r::g0 || $Slic3r::gcode_flavor eq 'mach3') && defined $params{move_z}) { + } elsif (($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3') && defined $params{move_z}) { # combine Z change and retraction my $travel = [undef, $params{move_z}, $retract->[2], 'change layer and retract']; $gcode .= $self->G0(@$travel); } else { $gcode .= $self->G1(@$retract); - if (defined $params{move_z} && $Slic3r::retract_lift > 0) { - my $travel = [undef, $params{move_z} + $Slic3r::retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift']; + if (defined $params{move_z} && $Slic3r::Config->retract_lift > 0) { + my $travel = [undef, $params{move_z} + $Slic3r::Config->retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift']; $gcode .= $self->G0(@$travel); $self->lifted(1); } elsif ($lift) { @@ -238,7 +238,7 @@ sub retract { # reset extrusion distance during retracts # this makes sure we leave sufficient precision in the firmware - $gcode .= $self->reset_e if $Slic3r::gcode_flavor !~ /^(?:mach3|makerbot)$/; + $gcode .= $self->reset_e if $Slic3r::Config->gcode_flavor !~ /^(?:mach3|makerbot)$/; return $gcode; } @@ -249,12 +249,12 @@ sub unretract { my $gcode = ""; if ($self->lifted) { - $gcode .= $self->G0(undef, $self->z - $Slic3r::retract_lift, 0, 'restore layer Z'); + $gcode .= $self->G0(undef, $self->z - $Slic3r::Config->retract_lift, 0, 'restore layer Z'); $self->lifted(0); } $self->speed('retract'); - $gcode .= $self->G0(undef, undef, ($Slic3r::retract_length + $Slic3r::retract_restart_extra), + $gcode .= $self->G0(undef, undef, ($Slic3r::Config->retract_length + $Slic3r::Config->retract_restart_extra), "compensate retraction"); return $gcode; @@ -264,22 +264,22 @@ sub reset_e { my $self = shift; $self->extrusion_distance(0); - return sprintf "G92 %s0%s\n", $Slic3r::extrusion_axis, ($Slic3r::gcode_comments ? ' ; reset extrusion distance' : '') - if $Slic3r::extrusion_axis && !$Slic3r::use_relative_e_distances; + return sprintf "G92 %s0%s\n", $Slic3r::Config->extrusion_axis, ($Slic3r::Config->gcode_comments ? ' ; reset extrusion distance' : '') + if $Slic3r::Config->extrusion_axis && !$Slic3r::Config->use_relative_e_distances; } sub set_acceleration { my $self = shift; my ($acceleration) = @_; - return "" unless $Slic3r::acceleration; + return "" unless $Slic3r::Config->acceleration; return sprintf "M201 E%s%s\n", - $acceleration, ($Slic3r::gcode_comments ? ' ; adjust acceleration' : ''); + $acceleration, ($Slic3r::Config->gcode_comments ? ' ; adjust acceleration' : ''); } sub G0 { my $self = shift; - return $self->G1(@_) if !($Slic3r::g0 || $Slic3r::gcode_flavor eq 'mach3'); + return $self->G1(@_) if !($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3'); return $self->_G0_G1("G0", @_); } @@ -295,8 +295,8 @@ sub _G0_G1 { if ($point) { $gcode .= sprintf " X%.${dec}f Y%.${dec}f", - ($point->x * $Slic3r::scaling_factor) + $self->shift_x, - ($point->y * $Slic3r::scaling_factor) + $self->shift_y; #** + ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x, + ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y; #** $self->last_pos($point); } if (defined $z && $z != $self->z) { @@ -315,13 +315,13 @@ sub G2_G3 { my $gcode = $orientation eq 'cw' ? "G2" : "G3"; $gcode .= sprintf " X%.${dec}f Y%.${dec}f", - ($point->x * $Slic3r::scaling_factor) + $self->shift_x, - ($point->y * $Slic3r::scaling_factor) + $self->shift_y; #** + ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x, + ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y; #** # XY distance of the center from the start position $gcode .= sprintf " I%.${dec}f J%.${dec}f", - ($center->[X] - $self->last_pos->[X]) * $Slic3r::scaling_factor, - ($center->[Y] - $self->last_pos->[Y]) * $Slic3r::scaling_factor; + ($center->[X] - $self->last_pos->[X]) * &Slic3r::SCALING_FACTOR, + ($center->[Y] - $self->last_pos->[Y]) * &Slic3r::SCALING_FACTOR; $self->last_pos($point); return $self->_Gx($gcode, $e, $comment); @@ -348,23 +348,23 @@ sub _Gx { # apply the speed reduction for print moves on bottom layer my $speed_f = $self->speeds->{$speed}; if ($e && $self->layer->id == 0 && $comment !~ /retract/) { - $speed_f = $Slic3r::first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ + $speed_f = $Slic3r::Config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ ? ($speed_f * $1/100) - : $Slic3r::first_layer_speed * 60; + : $Slic3r::Config->first_layer_speed * 60; } $gcode .= sprintf " F%.${dec}f", $speed_f; $self->last_speed($speed); } # output extrusion distance - if ($e && $Slic3r::extrusion_axis) { - $self->extrusion_distance(0) if $Slic3r::use_relative_e_distances; + if ($e && $Slic3r::Config->extrusion_axis) { + $self->extrusion_distance(0) if $Slic3r::Config->use_relative_e_distances; $self->extrusion_distance($self->extrusion_distance + $e); $self->total_extrusion_length($self->total_extrusion_length + $e); - $gcode .= sprintf " %s%.5f", $Slic3r::extrusion_axis, $self->extrusion_distance; + $gcode .= sprintf " %s%.5f", $Slic3r::Config->extrusion_axis, $self->extrusion_distance; } - $gcode .= sprintf " ; %s", $comment if $comment && $Slic3r::gcode_comments; + $gcode .= sprintf " ; %s", $comment if $comment && $Slic3r::Config->gcode_comments; if ($append_bridge_off) { $gcode .= "\n;_BRIDGE_FAN_END"; } @@ -379,7 +379,7 @@ sub set_tool { $self->extruder_idx($tool); return $self->retract - . (sprintf "T%d%s\n", $tool, ($Slic3r::gcode_comments ? ' ; change tool' : '')) + . (sprintf "T%d%s\n", $tool, ($Slic3r::Config->gcode_comments ? ' ; change tool' : '')) . $self->reset_e . $self->unretract; } @@ -391,10 +391,10 @@ sub set_fan { if ($self->last_fan_speed != $speed || $dont_save) { $self->last_fan_speed($speed) if !$dont_save; if ($speed == 0) { - return sprintf "M107%s\n", ($Slic3r::gcode_comments ? ' ; disable fan' : ''); + return sprintf "M107%s\n", ($Slic3r::Config->gcode_comments ? ' ; disable fan' : ''); } else { - return sprintf "M106 %s%d%s\n", ($Slic3r::gcode_flavor eq 'mach3' ? 'P' : 'S'), - (255 * $speed / 100), ($Slic3r::gcode_comments ? ' ; enable fan' : ''); + return sprintf "M106 %s%d%s\n", ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), + (255 * $speed / 100), ($Slic3r::Config->gcode_comments ? ' ; enable fan' : ''); } } return ""; @@ -404,13 +404,13 @@ sub set_temperature { my $self = shift; my ($temperature, $wait, $tool) = @_; - return "" if $wait && $Slic3r::gcode_flavor eq 'makerbot'; + return "" if $wait && $Slic3r::Config->gcode_flavor eq 'makerbot'; my ($code, $comment) = $wait ? ('M109', 'wait for temperature to be reached') : ('M104', 'set temperature'); return sprintf "$code %s%d %s; $comment\n", - ($Slic3r::gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature, + ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature, (defined $tool && $tool != $self->extruder_idx) ? "T$tool " : ""; } @@ -419,12 +419,12 @@ sub set_bed_temperature { my ($temperature, $wait) = @_; my ($code, $comment) = $wait - ? (($Slic3r::gcode_flavor eq 'makerbot' ? 'M109' - : $Slic3r::gcode_flavor eq 'teacup' ? 'M109 P1' + ? (($Slic3r::Config->gcode_flavor eq 'makerbot' ? 'M109' + : $Slic3r::Config->gcode_flavor eq 'teacup' ? 'M109 P1' : 'M190'), 'wait for bed temperature to be reached') : ('M140', 'set bed temperature'); return sprintf "$code %s%d ; $comment\n", - ($Slic3r::gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; + ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; } 1; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 7a27a74fe3..3b615659c1 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -31,6 +31,7 @@ use constant MI_CONF_WIZARD => 11; use constant MI_WEBSITE => 12; our $datadir; +our $Settings; our $small_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); $small_font->SetPointSize(11) if !&Wx::wxMSW; @@ -57,7 +58,7 @@ sub OnInit { # load settings if (-f "$datadir/slic3r.ini") { my $ini = eval { Slic3r::Config->read_ini("$datadir/slic3r.ini") }; - $Slic3r::Settings = $ini if $ini; + $Settings = $ini if $ini; } # application frame @@ -86,7 +87,7 @@ sub OnInit { $fileMenu->Append(MI_SLICE_SVG, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG'); $fileMenu->AppendSeparator(); $fileMenu->Append(wxID_EXIT, "&Quit", 'Quit Slic3r'); - EVT_MENU($frame, MI_LOAD_CONF, sub { $self->{skeinpanel}->load_config }); + EVT_MENU($frame, MI_LOAD_CONF, sub { $self->{skeinpanel}->load_config_file }); EVT_MENU($frame, MI_EXPORT_CONF, sub { $self->{skeinpanel}->save_config }); EVT_MENU($frame, MI_QUICK_SLICE, sub { $self->{skeinpanel}->do_slice; $repeat->Enable(defined $Slic3r::GUI::SkeinPanel::last_input_file) }); @@ -205,6 +206,12 @@ sub notify { $self->{notifier}->notify($message); } +sub save_settings { + my $self = shift; + + Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings); +} + package Slic3r::GUI::ProgressStatusBar; use Wx qw(:gauge :misc); use base 'Wx::StatusBar'; diff --git a/lib/Slic3r/GUI/ConfigWizard.pm b/lib/Slic3r/GUI/ConfigWizard.pm index 198d856bad..9af0eb60b4 100644 --- a/lib/Slic3r/GUI/ConfigWizard.pm +++ b/lib/Slic3r/GUI/ConfigWizard.pm @@ -372,7 +372,7 @@ sub apply { $self->SUPER::apply; # set print_center to centre of bed_size - my $bed_size = Slic3r::Config->get_raw('bed_size'); + my $bed_size = $Slic3r::Config->bed_size; Slic3r::Config->set('print_center', [$bed_size->[0]/2, $bed_size->[1]/2]); } @@ -395,7 +395,7 @@ sub apply { $self->SUPER::apply; # set first_layer_height + layer_height based on nozzle_diameter - my $nozzle = Slic3r::Config->get_raw('nozzle_diameter'); + my $nozzle = $Slic3r::Config->nozzle_diameter; Slic3r::Config->set('first_layer_height', $nozzle->[0]); Slic3r::Config->set('layer_height', $nozzle->[0] - 0.1); } @@ -435,7 +435,7 @@ sub apply { $self->SUPER::apply; # set first_layer_temperature to temperature + 5 - my $temperature = Slic3r::Config->get_raw('temperature'); + my $temperature = $Slic3r::Config->temperature; Slic3r::Config->set('first_layer_temperature', [$temperature->[0] + 5]); } @@ -459,7 +459,7 @@ sub apply { $self->SUPER::apply; # set first_layer_bed_temperature to temperature + 5 - my $temperature = Slic3r::Config->get_raw('bed_temperature'); + my $temperature = $Slic3r::Config->bed_temperature; Slic3r::Config->set('first_layer_bed_temperature', $temperature + 5); } diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm index 79e6f7cb7f..7e7009dda6 100644 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ b/lib/Slic3r/GUI/OptionsGroup.pm @@ -1,6 +1,7 @@ package Slic3r::GUI::OptionsGroup; use Moo; +use List::Util qw(first); use Wx qw(:combobox :font :misc :sizer :systemsettings :textctrl); use Wx::Event qw(EVT_CHECKBOX EVT_COMBOBOX EVT_SPINCTRL EVT_TEXT); @@ -28,6 +29,7 @@ Slic3r::GUI::OptionsGroup - pre-filled Wx::StaticBoxSizer wrapper containing one labels => [], values => [], default => 0.4, # mandatory + readonly => 0, on_change => sub { print "new value is $_[0]\n" }, }, ], @@ -86,6 +88,7 @@ sub BUILD { $field = $opt->{type} eq 'i' ? Wx::SpinCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style, $opt->{min} || 0, $opt->{max} || 100, $opt->{default}) : Wx::TextCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style); + $field->Disable if $opt->{readonly}; $self->_setters->{$opt_key} = sub { $field->SetValue($_[0]) }; my $on_change = sub { $self->_on_change($opt_key, $field->GetValue) }; @@ -145,6 +148,13 @@ sub BUILD { $self->sizer->Add($grid_sizer, 0, wxEXPAND); } +sub _option { + my $self = shift; + my ($opt_key) = @_; + + return first { $_->{opt_key} eq $opt_key } @{$self->options}; +} + sub _on_change { my $self = shift; my ($opt_key, $value) = @_; @@ -189,6 +199,7 @@ Slic3r::GUI::ConfigOptionsGroup - pre-filled Wx::StaticBoxSizer wrapper containi my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( parent => $self->parent, title => 'Layers', + config => $config, options => ['layer_height'], on_change => sub { print "new value for $_[0] is $_[1]\n" }, no_labels => 0, @@ -198,7 +209,7 @@ Slic3r::GUI::ConfigOptionsGroup - pre-filled Wx::StaticBoxSizer wrapper containi =cut -use List::Util qw(first); +has 'config' => (is => 'ro', required => 1); sub _trigger_options { my $self = shift; @@ -212,7 +223,7 @@ sub _trigger_options { $opt = { opt_key => $full_key, config => 1, - (map { $_ => $config_opt->{$_} } qw(type label tooltip sidetext width height full_width min max labels values multiline)), + (map { $_ => $config_opt->{$_} } qw(type label tooltip sidetext width height full_width min max labels values multiline readonly)), default => $self->_get_config($opt_key, $index), on_change => sub { $self->_set_config($opt_key, $index, $_[0]) }, }; @@ -225,7 +236,10 @@ sub set_value { my $self = shift; my ($opt_key, $value) = @_; - if (first { $_->{opt_key} eq $opt_key && !$_->{config} } @{$self->options}) { + my $opt = $self->_option($opt_key) or return 0; + + # if user is setting a non-config option, forward the call to the parent + if (!$opt->{config}) { return $self->SUPER::set_value($opt_key, $value); } @@ -234,7 +248,8 @@ sub set_value { my ($key, $index) = $self->_split_key($full_key); if ($key eq $opt_key) { - $self->SUPER::set_value($full_key, $self->_get_config($key, $index, $value)); + $self->config->set($key, $value); + $self->SUPER::set_value($full_key, $self->_get_config($key, $index)); $changed = 1; } } @@ -252,11 +267,14 @@ sub _split_key { sub _get_config { my $self = shift; - my ($opt_key, $index, $value) = @_; + my ($opt_key, $index) = @_; - my ($get_m, $set_m) = $self->_config_methods($opt_key, $index); - $value ||= Slic3r::Config->$get_m($opt_key); - $value = $value->[$index] if defined $index; + my ($get_m, $serialized) = $self->_config_methods($opt_key, $index); + my $value = $self->config->$get_m($opt_key); + if (defined $index) { + $value->[$index] //= $value->[0]; #/ + $value = $value->[$index]; + } return $value; } @@ -264,10 +282,10 @@ sub _set_config { my $self = shift; my ($opt_key, $index, $value) = @_; - my ($get_m, $set_m) = $self->_config_methods($opt_key, $index); + my ($get_m, $serialized) = $self->_config_methods($opt_key, $index); defined $index - ? Slic3r::Config->$get_m($opt_key)->[$index] = $value - : Slic3r::Config->$set_m($opt_key, $value); + ? $self->config->$get_m($opt_key)->[$index] = $value + : $self->config->set($opt_key, $value, $serialized); } sub _config_methods { @@ -275,9 +293,9 @@ sub _config_methods { my ($opt_key, $index) = @_; # if it's an array type but no index was specified, use the serialized version - return $Slic3r::Config::Options->{$opt_key}{type} =~ /\@$/ && !defined $index - ? qw(serialize deserialize) - : qw(get_raw set); + return ($Slic3r::Config::Options->{$opt_key}{type} =~ /\@$/ && !defined $index) + ? qw(serialize 1) + : qw(get 0); } 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 227d163121..e843819b31 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -26,10 +26,17 @@ my $MESSAGE_DIALOG_EVENT : shared = Wx::NewEventType; my $EXPORT_COMPLETED_EVENT : shared = Wx::NewEventType; my $EXPORT_FAILED_EVENT : shared = Wx::NewEventType; +use constant CANVAS_TEXT => join('-', +(localtime)[3,4]) eq '13-8' + ? 'What do you want to print today? ™' # Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap. + : 'Drag your objects here'; + sub new { my $class = shift; my ($parent) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + $self->{config} = Slic3r::Config->new_from_defaults(qw( + bed_size print_center complete_objects extruder_clearance_radius skirts skirt_distance + )); $self->{canvas} = Wx::Panel->new($self, -1, wxDefaultPosition, [300, 300], wxTAB_TRAVERSAL); $self->{canvas}->SetBackgroundColour(Wx::wxWHITE); @@ -173,7 +180,7 @@ sub new { $self->on_export_failed; }); - $self->update_bed_size; + $self->_update_bed_size; $self->{print} = Slic3r::Print->new; $self->{thumbnails} = []; # polygons, each one aligned to 0,0 $self->{scale} = []; @@ -229,6 +236,11 @@ sub new { return $self; } +sub skeinpanel { + my $self = shift; + return $self->GetParent->GetParent; +} + sub load { my $self = shift; @@ -428,7 +440,7 @@ sub split_object { my $current_object = $self->{print}->objects->[$obj_idx]; my $current_copies_num = @{$self->{print}->copies->[$obj_idx]}; my $mesh = $current_object->mesh->clone; - $mesh->scale($Slic3r::scaling_factor); + $mesh->scale(&Slic3r::SCALING_FACTOR); my @new_meshes = $mesh->split_mesh; if (@new_meshes == 1) { @@ -517,10 +529,9 @@ sub export_gcode2 { } if $Slic3r::have_threads; eval { - # validate configuration - Slic3r::Config->validate; - my $print = $self->{print}; + $print->config($self->skeinpanel->config); + $print->config->validate; $print->validate; { @@ -607,7 +618,7 @@ sub export_stl { push @{$mesh->facets}, map [ $_->[0], map $vertices_offset + $_, @$_[-3..-1] ], @{$cloned_mesh->facets}; } } - $mesh->scale($Slic3r::scaling_factor); + $mesh->scale(&Slic3r::SCALING_FACTOR); $mesh->align_to_origin; Slic3r::Format::STL->write_file($output_file, $mesh, 1); @@ -657,14 +668,21 @@ sub recenter { ]; } -sub update_bed_size { +sub on_config_change { + my $self = shift; + my ($opt_key, $value) = @_; + $self->{config}->set($opt_key, $value) if exists $self->{config}{$opt_key}; + $self->_update_bed_size; +} + +sub _update_bed_size { my $self = shift; # supposing the preview canvas is square, calculate the scaling factor # to constrain print bed area inside preview + my $bed_size = $self->{config}->bed_size; my $canvas_side = $self->{canvas}->GetSize->GetWidth; - my $bed_largest_side = $Slic3r::bed_size->[X] > $Slic3r::bed_size->[Y] - ? $Slic3r::bed_size->[X] : $Slic3r::bed_size->[Y]; + my $bed_largest_side = $bed_size->[X] > $bed_size->[Y] ? $bed_size->[X] : $bed_size->[Y]; my $old_scaling_factor = $self->{scaling_factor}; $self->{scaling_factor} = $canvas_side / $bed_largest_side; if (defined $old_scaling_factor && $self->{scaling_factor} != $old_scaling_factor) { @@ -672,6 +690,7 @@ sub update_bed_size { } } +# this is called on the canvas sub repaint { my ($self, $event) = @_; my $parent = $self->GetParent; @@ -681,9 +700,6 @@ sub repaint { my $size = $self->GetSize; my @size = ($size->GetWidth, $size->GetHeight); - # calculate scaling factor for preview - $parent->update_bed_size; - # draw grid $dc->SetPen($parent->{grid_pen}); my $step = 10 * $parent->{scaling_factor}; @@ -701,8 +717,8 @@ sub repaint { $dc->DrawLine(0, $size[Y]/2, $size[X], $size[Y]/2); $dc->SetTextForeground(Wx::Colour->new(0,0,0)); $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawLabel("X = " . $Slic3r::print_center->[X], Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM); - $dc->DrawRotatedText("Y = " . $Slic3r::print_center->[Y], 0, $size[Y]/2+15, 90); + $dc->DrawLabel("X = " . $parent->{config}->print_center->[X], Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM); + $dc->DrawRotatedText("Y = " . $parent->{config}->print_center->[Y], 0, $size[Y]/2+15, 90); } # draw frame @@ -714,7 +730,7 @@ sub repaint { if (!@{$print->objects}) { $dc->SetTextForeground(Wx::Colour->new(150,50,50)); $dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawLabel("Drag your objects here", Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); + $dc->DrawLabel(CANVAS_TEXT, Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); } # draw thumbnails @@ -738,8 +754,8 @@ sub repaint { $dc->DrawPolygon($parent->_y($parent->{object_previews}->[-1][2]), 0, 0); # if sequential printing is enabled and we have more than one object - if ($Slic3r::complete_objects && (map @$_, @{$print->copies}) > 1) { - my $clearance = +($parent->{object_previews}->[-1][2]->offset($Slic3r::extruder_clearance_radius / 2 * $parent->{scaling_factor}, 1, JT_ROUND))[0]; + if ($parent->{config}->complete_objects && (map @$_, @{$print->copies}) > 1) { + my $clearance = +($parent->{object_previews}->[-1][2]->offset($parent->{config}->extruder_clearance_radius / 2 * $parent->{scaling_factor}, 1, JT_ROUND))[0]; $dc->SetPen($parent->{clearance_pen}); $dc->SetBrush($parent->{transparent_brush}); $dc->DrawPolygon($parent->_y($clearance), 0, 0); @@ -748,9 +764,9 @@ sub repaint { } # draw skirt - if (@{$parent->{object_previews}} && $Slic3r::skirts) { + if (@{$parent->{object_previews}} && $parent->{config}->skirts) { my $convex_hull = Slic3r::Polygon->new(convex_hull([ map @{$_->[2]}, @{$parent->{object_previews}} ])); - $convex_hull = +($convex_hull->offset($Slic3r::skirt_distance * $parent->{scaling_factor}, 1, JT_ROUND))[0]; + $convex_hull = +($convex_hull->offset($parent->{config}->skirt_distance * $parent->{scaling_factor}, 1, JT_ROUND))[0]; $dc->SetPen($parent->{skirt_pen}); $dc->SetBrush($parent->{transparent_brush}); $dc->DrawPolygon($parent->_y($convex_hull), 0, 0) if $convex_hull; @@ -853,7 +869,7 @@ sub selected_object_idx { sub statusbar { my $self = shift; - return $self->GetParent->GetParent->GetParent->{statusbar}; + return $self->skeinpanel->GetParent->{statusbar}; } sub to_pixel { diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index 98b1ed5ad9..8ed14c8008 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -27,6 +27,10 @@ sub new { filament => Slic3r::GUI::Tab::Filament->new ($self->{tabpanel}, sync_presets_with => $self->{plater}{preset_choosers}{filament}), printer => Slic3r::GUI::Tab::Printer->new ($self->{tabpanel}, sync_presets_with => $self->{plater}{preset_choosers}{printer}), }; + + # propagate config change events to the plater + $_->{on_value_change} = sub { $self->{plater}->on_config_change(@_) } for values %{$self->{options_tabs}}; + $self->{tabpanel}->AddPage($self->{options_tabs}{print}, $self->{options_tabs}{print}->title); $self->{tabpanel}->AddPage($self->{options_tabs}{filament}, $self->{options_tabs}{filament}->title); $self->{tabpanel}->AddPage($self->{options_tabs}{printer}, $self->{options_tabs}{printer}->title); @@ -53,11 +57,12 @@ sub do_slice { my $process_dialog; eval { # validate configuration - Slic3r::Config->validate; + my $config = $self->config; + $config->validate; # confirm slicing of more than one copies - my $copies = $Slic3r::duplicate_grid->[X] * $Slic3r::duplicate_grid->[Y]; - $copies = $Slic3r::duplicate if $Slic3r::duplicate > 1; + my $copies = $Slic3r::Config->duplicate_grid->[X] * $Slic3r::Config->duplicate_grid->[Y]; + $copies = $Slic3r::Config->duplicate if $Slic3r::Config->duplicate > 1; if ($copies > 1) { my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?", 'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL); @@ -93,7 +98,7 @@ sub do_slice { my $input_file_basename = basename($input_file); $last_skein_dir = dirname($input_file); - my $print = Slic3r::Print->new; + my $print = Slic3r::Print->new(config => $config); $print->add_object_from_file($input_file); $print->validate; @@ -160,12 +165,12 @@ sub do_slice { sub save_config { my $self = shift; - my $process_dialog; + my $config = $self->config; eval { # validate configuration - Slic3r::Config->validate; + $config->validate; }; - Slic3r::GUI::catch_error($self, sub { $process_dialog->Destroy if $process_dialog }) and return; + Slic3r::GUI::catch_error($self) and return; my $dir = $last_config ? dirname($last_config) : $last_config_dir || $last_skein_dir || ""; my $filename = $last_config ? basename($last_config) : "config.ini"; @@ -175,12 +180,12 @@ sub save_config { my $file = $dlg->GetPath; $last_config_dir = dirname($file); $last_config = $file; - Slic3r::Config->save($file); + $config->save($file); } $dlg->Destroy; } -sub load_config { +sub load_config_file { my $self = shift; my ($file) = @_; @@ -198,15 +203,40 @@ sub load_config { $_->load_external_config($file) for values %{$self->{options_tabs}}; } +sub load_config { + my $self = shift; + my ($config) = @_; + + foreach my $tab (values %{$self->{options_tabs}}) { + $tab->set_value($_, $config->$_) for keys %$config; + } +} + sub config_wizard { my $self = shift; return unless $self->check_unsaved_changes; - if (my %settings = Slic3r::GUI::ConfigWizard->new($self)->run) { - $self->set_value($_, $settings{$_}) for keys %settings; + if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) { + # TODO: select the default preset in all tabs + $self->load_config($config); } } +=head2 config + +This method collects all config values from the tabs and merges them into a single config object. + +=cut + +sub config { + my $self = shift; + + return Slic3r::Config->merge( + Slic3r::Config->new_from_defaults, + (map $_->config, values %{$self->{options_tabs}}), + ); +} + sub set_value { my $self = shift; my ($opt_key, $value) = @_; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 450a8dadb2..7ce14b89bc 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -13,6 +13,7 @@ sub new { my $class = shift; my ($parent, %params) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); + $self->{options} = []; # array of option names handled by this tab $self->{sync_presets_with} = $params{sync_presets_with}; EVT_CHOICE($parent, $self->{sync_presets_with}, sub { @@ -100,8 +101,8 @@ sub new { ); return unless $dlg->ShowModal == wxID_OK; - my $file = sprintf "$Slic3r::GUI::datadir/$self->{presets_group}/%s.ini", $dlg->get_name; - Slic3r::Config->save($file, $self->{presets_group}); + my $file = sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->name, $dlg->get_name; + $self->{config}->save($file); $self->set_dirty(0); $self->load_presets; $self->{presets_choice}->SetSelection(first { basename($self->{presets}[$_]{file}) eq $dlg->get_name . ".ini" } 1 .. $#{$self->{presets}}); @@ -125,7 +126,13 @@ sub new { $self->sync_presets; }); + $self->{config} = Slic3r::Config->new; $self->build; + if ($self->hidden_options) { + $self->{config}->apply(Slic3r::Config->new_from_defaults($self->hidden_options)); + push @{$self->{options}}, $self->hidden_options; + } + $self->load_presets; return $self; } @@ -135,8 +142,15 @@ sub current_preset { return $self->{presets}[ $self->{presets_choice}->GetSelection ]; } -sub on_value_change {} +# propagate event to the parent +sub on_value_change { + my $self = shift; + $self->{on_value_change}->(@_) if $self->{on_value_change}; +} + sub on_preset_loaded {} +sub hidden_options {} +sub config { $_[0]->{config}->clone } sub on_select_preset { my $self = shift; @@ -155,7 +169,7 @@ sub on_select_preset { my $preset = $self->current_preset; if ($preset->{default}) { # default settings: disable the delete button - Slic3r::Config->load_hash($Slic3r::Defaults, $self->{presets_group}, 1); + $self->{config}->apply(Slic3r::Config->new_from_defaults(@{$self->{options}})); $self->{btn_delete_preset}->Disable; } else { if (!-e $preset->{file}) { @@ -164,7 +178,7 @@ sub on_select_preset { } eval { local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); - Slic3r::Config->load($preset->{file}, $self->{presets_group}); + $self->{config}->apply(Slic3r::Config->load($preset->{file})); }; Slic3r::GUI::catch_error($self); $preset->{external} @@ -174,8 +188,8 @@ sub on_select_preset { $self->on_preset_loaded; $self->reload_values; $self->set_dirty(0); - $Slic3r::Settings->{presets}{$self->{presets_group}} = $preset->{file} ? basename($preset->{file}) : ''; - Slic3r::Config->save_settings("$Slic3r::GUI::datadir/slic3r.ini"); + $Slic3r::GUI::Settings->{presets}{$self->name} = $preset->{file} ? basename($preset->{file}) : ''; + Slic3r::GUI->save_settings("$Slic3r::GUI::datadir/slic3r.ini"); } sub add_options_page { @@ -188,6 +202,19 @@ sub add_options_page { $self->{iconcount}++; } + { + # get all config options being added to the current page; remove indexes; associate defaults + my @options = map { $_ =~ s/#.+//; $_ } grep !ref($_), map @{$_->{options}}, @{$params{optgroups}}; + my %defaults_to_set = map { $_ => 1 } @options; + + # apply default values for the options we don't have already + delete $defaults_to_set{$_} for @{$self->{options}}; + $self->{config}->apply(Slic3r::Config->new_from_defaults(keys %defaults_to_set)) if %defaults_to_set; + + # append such options to our list + push @{$self->{options}}, @options; + } + my $page = Slic3r::GUI::Tab::Page->new($self, $title, $self->{iconcount}, %params, on_change => sub { $self->on_value_change(@_); $self->set_dirty(1); @@ -214,8 +241,7 @@ sub set_value { sub reload_values { my $self = shift; - my $current = Slic3r::Config->current; - $self->set_value($_, $current->{$_}) for keys %$current; + $self->set_value($_, $self->{config}->get($_)) for keys %{$self->{config}}; } sub update_tree { @@ -262,20 +288,18 @@ sub is_dirty { sub load_presets { my $self = shift; - my ($group) = @_; - $self->{presets_group} ||= $group; $self->{presets} = [{ default => 1, name => '- default -', }]; - opendir my $dh, "$Slic3r::GUI::datadir/$self->{presets_group}" or die "Failed to read directory $Slic3r::GUI::datadir/$self->{presets_group} (errno: $!)\n"; + opendir my $dh, "$Slic3r::GUI::datadir/" . $self->name or die "Failed to read directory $Slic3r::GUI::datadir/" . $self->name . " (errno: $!)\n"; foreach my $file (sort grep /\.ini$/i, readdir $dh) { my $name = basename($file); $name =~ s/\.ini$//; push @{$self->{presets}}, { - file => "$Slic3r::GUI::datadir/$self->{presets_group}/$file", + file => "$Slic3r::GUI::datadir/" . $self->name . "/$file", name => $name, }; } @@ -285,7 +309,7 @@ sub load_presets { $self->{presets_choice}->Append($_->{name}) for @{$self->{presets}}; { # load last used preset - my $i = first { basename($self->{presets}[$_]{file}) eq ($Slic3r::Settings->{presets}{$self->{presets_group}} || '') } 1 .. $#{$self->{presets}}; + my $i = first { basename($self->{presets}[$_]{file}) eq ($Slic3r::GUI::Settings->{presets}{$self->name} || '') } 1 .. $#{$self->{presets}}; $self->{presets_choice}->SetSelection($i || 0); $self->on_select_preset; } @@ -326,6 +350,7 @@ sub sync_presets { package Slic3r::GUI::Tab::Print; use base 'Slic3r::GUI::Tab'; +sub name { 'print' } sub title { 'Print Settings' } sub build { @@ -422,16 +447,17 @@ sub build { }, { title => 'Other', - options => [qw(duplicate_distance), ($Slic3r::have_threads ? qw(threads) : ())], + options => [($Slic3r::have_threads ? qw(threads) : ())], }, ]); - - $self->load_presets('print'); } +sub hidden_options { !$Slic3r::have_threads ? qw(threads) : () } + package Slic3r::GUI::Tab::Filament; use base 'Slic3r::GUI::Tab'; +sub name { 'filament' } sub title { 'Filament Settings' } sub build { @@ -463,13 +489,12 @@ sub build { options => [qw(fan_below_layer_time slowdown_below_layer_time min_print_speed)], }, ]); - - $self->load_presets('filament'); } package Slic3r::GUI::Tab::Printer; use base 'Slic3r::GUI::Tab'; +sub name { 'printer' } sub title { 'Printer Settings' } sub build { @@ -521,22 +546,28 @@ sub build { ]); $self->{extruder_pages} = []; - $self->build_extruder_pages; - - $self->load_presets('printer'); + $self->_build_extruder_pages; } -sub extruder_options { qw(nozzle_diameter) } +sub _extruder_options { qw(nozzle_diameter) } -sub build_extruder_pages { +sub config { + my $self = shift; + + my $config = $self->SUPER::config(@_); + + # remove all unused values + foreach my $opt_key ($self->_extruder_options) { + splice @{ $config->{$opt_key} }, $self->{extruders_count}; + } + + return $config; +} + +sub _build_extruder_pages { my $self = shift; foreach my $extruder_idx (0 .. $self->{extruders_count}-1) { - # set default values - for my $opt_key ($self->extruder_options) { - Slic3r::Config->get_raw($opt_key)->[$extruder_idx] //= Slic3r::Config->get_raw($opt_key)->[0]; #/ - } - # build page if it doesn't exist $self->{extruder_pages}[$extruder_idx] ||= $self->add_options_page("Extruder " . ($extruder_idx + 1), 'funnel.png', optgroups => [ { @@ -571,28 +602,22 @@ sub on_value_change { $page->{disabled} = 1; } - # delete values for unused extruders - for my $opt_key ($self->extruder_options) { - my $values = Slic3r::Config->get_raw($opt_key); - splice @$values, $self->{extruders_count} if $self->{extruders_count} <= $#$values; - } - # add extra pages - $self->build_extruder_pages; + $self->_build_extruder_pages; # update page list and select first page (General) $self->update_tree(0); } } -# this gets executed after preset is loaded in repository and before GUI fields are updated +# this gets executed after preset is loaded and before GUI fields are updated sub on_preset_loaded { my $self = shift; # update the extruders count field { # update the GUI field according to the number of nozzle diameters supplied - $self->set_value('extruders_count', scalar @{ Slic3r::Config->get_raw('nozzle_diameter') }); + $self->set_value('extruders_count', scalar @{ $self->{config}->nozzle_diameter }); # update extruder page list $self->on_value_change('extruders_count'); @@ -617,7 +642,11 @@ sub new { $self->SetSizer($self->{vsizer}); if ($params{optgroups}) { - $self->append_optgroup(%$_, on_change => $params{on_change}) for @{$params{optgroups}}; + $self->append_optgroup( + %$_, + config => $parent->{config}, + on_change => $params{on_change}, + ) for @{$params{optgroups}}; } return $self; @@ -627,7 +656,12 @@ sub append_optgroup { my $self = shift; my %params = @_; - my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(parent => $self, label_width => 200, %params); + my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( + parent => $self, + config => $self->GetParent->{config}, + label_width => 200, + %params, + ); $self->{vsizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 5); push @{$self->{optgroups}}, $optgroup; } diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 1b873ce173..979a2eb0ef 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -39,8 +39,8 @@ our $parallel_degrees_limit = abs(deg2rad(3)); sub epsilon () { 1E-4 } -sub scale ($) { $_[0] / $Slic3r::scaling_factor } -sub unscale ($) { $_[0] * $Slic3r::scaling_factor } +sub scale ($) { $_[0] / &Slic3r::SCALING_FACTOR } +sub unscale ($) { $_[0] * &Slic3r::SCALING_FACTOR } sub slope { my ($line) = @_; @@ -893,8 +893,8 @@ sub arrange { # margin needed for the skirt my $skirt_margin; - if ($Slic3r::skirts > 0) { - $skirt_margin = ($Slic3r::flow->spacing * $Slic3r::skirts + $Slic3r::skirt_distance) * 2; + if ($Slic3r::Config->skirts > 0) { + $skirt_margin = ($Slic3r::flow->spacing * $Slic3r::Config->skirts + $Slic3r::Config->skirt_distance) * 2; } else { $skirt_margin = 0; } diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index dc076bb042..2bd51d873d 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -20,7 +20,7 @@ has 'slice_z' => (is => 'lazy'); has 'print_z' => (is => 'lazy'); has 'height' => (is => 'lazy'); has 'flow' => (is => 'lazy'); -has 'perimeters_flow' => (is => 'lazy'); +has 'perimeter_flow' => (is => 'lazy'); has 'infill_flow' => (is => 'lazy'); # collection of spare segments generated by slicing the original geometry; @@ -61,21 +61,21 @@ sub _build_slice_z { my $self = shift; if ($self->id == 0) { - return $Slic3r::_first_layer_height / 2 / $Slic3r::scaling_factor; + return $Slic3r::Config->get_value('first_layer_height') / 2 / &Slic3r::SCALING_FACTOR; } - return ($Slic3r::_first_layer_height + (($self->id-1) * $Slic3r::layer_height) + ($Slic3r::layer_height/2)) - / $Slic3r::scaling_factor; #/ + return ($Slic3r::Config->get_value('first_layer_height') + (($self->id-1) * $Slic3r::Config->layer_height) + ($Slic3r::Config->layer_height/2)) + / &Slic3r::SCALING_FACTOR; #/ } # Z used for printing sub _build_print_z { my $self = shift; - return ($Slic3r::_first_layer_height + ($self->id * $Slic3r::layer_height)) / $Slic3r::scaling_factor; + return ($Slic3r::Config->get_value('first_layer_height') + ($self->id * $Slic3r::Config->layer_height)) / &Slic3r::SCALING_FACTOR; } sub _build_height { my $self = shift; - return $self->id == 0 ? $Slic3r::_first_layer_height : $Slic3r::layer_height; + return $self->id == 0 ? $Slic3r::Config->get_value('first_layer_height') : $Slic3r::Config->layer_height; } sub _build_flow { @@ -85,11 +85,11 @@ sub _build_flow { : $Slic3r::flow; } -sub _build_perimeters_flow { +sub _build_perimeter_flow { my $self = shift; return $self->id == 0 && $Slic3r::first_layer_flow ? $Slic3r::first_layer_flow - : $Slic3r::perimeters_flow; + : $Slic3r::perimeter_flow; } sub _build_infill_flow { @@ -120,7 +120,7 @@ sub make_surfaces { # the contours must be offsetted by half extrusion width inwards { - my $distance = scale $self->perimeters_flow->width / 2; + my $distance = scale $self->perimeter_flow->width / 2; my @surfaces = @{$self->slices}; @{$self->slices} = (); foreach my $surface (@surfaces) { @@ -141,10 +141,10 @@ sub make_surfaces { $self->thin_walls([]); if (@$diff) { - my $area_threshold = scale($self->perimeters_flow->spacing) ** 2; + my $area_threshold = scale($self->perimeter_flow->spacing) ** 2; @$diff = grep $_->area > ($area_threshold), @$diff; - @{$self->thin_walls} = map $_->medial_axis(scale $self->perimeters_flow->width), @$diff; + @{$self->thin_walls} = map $_->medial_axis(scale $self->perimeter_flow->width), @$diff; Slic3r::debugf " %d thin walls detected\n", scalar(@{$self->thin_walls}) if @{$self->thin_walls}; } @@ -163,7 +163,7 @@ sub make_perimeters { my $self = shift; Slic3r::debugf "Making perimeters for layer %d\n", $self->id; - my $gap_area_threshold = scale($self->perimeters_flow->width)** 2; + my $gap_area_threshold = scale($self->perimeter_flow->width)** 2; # this array will hold one arrayref per original surface (island); # each item of this arrayref is an arrayref representing a depth (from outer @@ -197,7 +197,7 @@ sub make_perimeters { if (0) { foreach my $hole ($last_offsets[0]->holes) { my $circumference = abs($hole->length); - next unless $circumference <= $Slic3r::small_perimeter_length; + next unless $circumference <= &Slic3r::SMALL_PERIMETER_LENGTH; # this compensation only works for circular holes, while it would # overcompensate for hexagons and other shapes having straight edges. # so we require a minimum number of vertices. @@ -205,8 +205,8 @@ sub make_perimeters { # revert the compensation done in make_surfaces() and get the actual radius # of the hole - my $radius = ($circumference / PI / 2) - scale $self->perimeters_flow->spacing/2; - my $new_radius = (scale($self->perimeters_flow->width) + sqrt((scale($self->perimeters_flow->width)**2) + (4*($radius**2)))) / 2; + my $radius = ($circumference / PI / 2) - scale $self->perimeter_flow->spacing/2; + my $new_radius = (scale($self->perimeter_flow->width) + sqrt((scale($self->perimeter_flow->width)**2) + (4*($radius**2)))) / 2; # holes are always turned to contours, so reverse point order before and after $hole->reverse; my @offsetted = $hole->offset(+ ($new_radius - $radius)); @@ -219,7 +219,7 @@ sub make_perimeters { my @gaps = (); # generate perimeters inwards - my $loop_number = $Slic3r::perimeters + ($surface->additional_inner_perimeters || 0); + my $loop_number = $Slic3r::Config->perimeters + ($surface->additional_inner_perimeters || 0); push @perimeters, []; for (my $loop = 0; $loop < $loop_number; $loop++) { # offsetting a polygon can result in one or many offset polygons @@ -241,19 +241,19 @@ sub make_perimeters { push @{ $perimeters[-1] }, [@last_offsets]; # offset distance for inner loops - $distance = scale $self->perimeters_flow->spacing; + $distance = scale $self->perimeter_flow->spacing; } # create one more offset to be used as boundary for fill { my @fill_boundaries = map $_->offset_ex(-$distance), @last_offsets; - $_->simplify(scale $Slic3r::resolution) for @fill_boundaries; + $_->simplify(scale &Slic3r::RESOLUTION) for @fill_boundaries; push @{ $self->surfaces }, @fill_boundaries; # detect the small gaps that we need to treat like thin polygons, # thus generating the skeleton and using it to fill them push @{ $self->thin_fills }, - map $_->medial_axis(scale $self->perimeters_flow->width), + map $_->medial_axis(scale $self->perimeter_flow->width), @gaps; Slic3r::debugf " %d gaps filled\n", scalar @{ $self->thin_fills } if @{ $self->thin_fills }; @@ -323,7 +323,7 @@ sub make_perimeters { my @thin_paths = (); my %properties = ( role => EXTR_ROLE_PERIMETER, - flow_spacing => $self->perimeters_flow->spacing, + flow_spacing => $self->perimeter_flow->spacing, ); for (@{ $self->thin_walls }) { push @thin_paths, $_->isa('Slic3r::Polygon') @@ -339,11 +339,11 @@ sub _add_perimeter { my $self = shift; my ($polygon, $role) = @_; - return unless $polygon->is_printable($self->perimeters_flow->width); + return unless $polygon->is_printable($self->perimeter_flow->width); push @{ $self->perimeters }, Slic3r::ExtrusionLoop->pack( polygon => $polygon, - role => (abs($polygon->length) <= $Slic3r::small_perimeter_length) ? EXTR_ROLE_SMALLPERIMETER : ($role // EXTR_ROLE_PERIMETER), #/ - flow_spacing => $self->perimeters_flow->spacing, + role => (abs($polygon->length) <= &Slic3r::SMALL_PERIMETER_LENGTH) ? EXTR_ROLE_SMALLPERIMETER : ($role // EXTR_ROLE_PERIMETER), #/ + flow_spacing => $self->perimeter_flow->spacing, ); } @@ -354,18 +354,18 @@ sub prepare_fill_surfaces { # if no solid layers are requested, turn top/bottom surfaces to internal # note that this modifies $self->surfaces in place - if ($Slic3r::solid_layers == 0) { + if ($Slic3r::Config->solid_layers == 0) { $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type != S_TYPE_INTERNAL, @surfaces; } # if hollow object is requested, remove internal surfaces - if ($Slic3r::fill_density == 0) { + if ($Slic3r::Config->fill_density == 0) { @surfaces = grep $_->surface_type != S_TYPE_INTERNAL, @surfaces; } # turn too small internal regions into solid regions { - my $min_area = ((7 * $self->infill_flow->spacing / $Slic3r::scaling_factor)**2) * PI; + my $min_area = ((7 * $self->infill_flow->spacing / &Slic3r::SCALING_FACTOR)**2) * PI; my @small = grep $_->surface_type == S_TYPE_INTERNAL && $_->expolygon->contour->area <= $min_area, @surfaces; $_->surface_type(S_TYPE_INTERNALSOLID) for @small; Slic3r::debugf "identified %d small surfaces at layer %d\n", scalar(@small), $self->id if @small > 0; @@ -379,7 +379,7 @@ sub process_bridges { my $self = shift; # no bridges are possible if we have no internal surfaces - return if $Slic3r::fill_density == 0; + return if $Slic3r::Config->fill_density == 0; my @bridges = (); diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 6de3772876..fa7ee712d8 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -72,7 +72,7 @@ sub safety_offset { sub offset { my $self = shift; my ($distance, $scale, $joinType, $miterLimit) = @_; - $scale ||= $Slic3r::scaling_factor * 1000000; + $scale ||= &Slic3r::SCALING_FACTOR * 1000000; $joinType = JT_MITER if !defined $joinType; $miterLimit ||= 2; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 639e3b454f..093f2eb07c 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -8,6 +8,7 @@ use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 PI scale unscale move_points); use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex offset JT_ROUND); use Time::HiRes qw(gettimeofday tv_interval); +has 'config' => (is => 'rw', default => sub { Slic3r::Config->new_from_defaults }); has 'objects' => (is => 'rw', default => sub {[]}); has 'copies' => (is => 'rw', default => sub {[]}); # obj_idx => [copies...] has 'total_extrusion_length' => (is => 'rw'); @@ -27,6 +28,49 @@ has 'brim' => ( default => sub { [] }, ); +sub BUILD { + my $self = shift; + + # store config in a handy place + $Slic3r::Config = $self->config; + + # initialize extruder(s) + $Slic3r::extruders = []; + for my $t (0, map $_-1, map $self->config->get($_), qw(perimeter_extruder infill_extruder support_material_extruder)) { + $Slic3r::extruders->[$t] ||= Slic3r::Extruder->new( + map { $_ => $self->config->get($_)->[$t] // $self->config->get($_)->[0] } #/ + qw(nozzle_diameter filament_diameter extrusion_multiplier temperature first_layer_temperature) + ); + } + + # calculate flow + $Slic3r::flow = $Slic3r::extruders->[0]->make_flow(width => $self->config->extrusion_width); + if ($self->config->first_layer_extrusion_width) { + $Slic3r::first_layer_flow = $Slic3r::extruders->[0]->make_flow( + layer_height => $self->config->get_value('first_layer_height'), + width => $self->config->first_layer_extrusion_width, + ); + } + for (qw(perimeter infill support_material)) { + no strict 'refs'; + ${"Slic3r::${_}_flow"} = $Slic3r::extruders->[ $self->config->get("${_}_extruder")-1 ] + ->make_flow(width => $self->config->get("${_}_extrusion_width") || $self->config->extrusion_width); + } + + Slic3r::debugf "Default flow width = %s (spacing = %s)\n", + $Slic3r::flow->width, $Slic3r::flow->spacing; + + # G-code flavors + $self->config->set('extrusion_axis', 'A') if $self->config->gcode_flavor eq 'mach3'; + $self->config->set('extrusion_axis', '') if $self->config->gcode_flavor eq 'no-extrusion'; + + # legacy with existing config files + $self->config->set_ifndef('small_perimeter_speed', $self->config->perimeter_speed); + $self->config->set_ifndef('bridge_speed', $self->config->infill_speed); + $self->config->set_ifndef('solid_infill_speed', $self->config->infill_speed); + $self->config->set_ifndef('top_solid_infill_speed', $self->config->solid_infill_speed); +} + sub add_object_from_file { my $self = shift; my ($input_file) = @_; @@ -55,8 +99,8 @@ sub add_object_from_mesh { my $self = shift; my ($mesh) = @_; - $mesh->rotate($Slic3r::rotate); - $mesh->scale($Slic3r::scale / $Slic3r::scaling_factor); + $mesh->rotate($Slic3r::Config->rotate); + $mesh->scale($Slic3r::Config->scale / &Slic3r::SCALING_FACTOR); $mesh->align_to_origin; # initialize print object @@ -75,7 +119,7 @@ sub add_object_from_mesh { sub validate { my $self = shift; - if ($Slic3r::complete_objects) { + if ($Slic3r::Config->complete_objects) { # check horizontal clearance { my @a = (); @@ -84,7 +128,7 @@ sub validate { { my @points = map [ @$_[X,Y] ], @{$self->objects->[$obj_idx]->mesh->vertices}; my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); - $clearance = +($convex_hull->offset(scale $Slic3r::extruder_clearance_radius / 2, 1, JT_ROUND))[0]; + $clearance = +($convex_hull->offset(scale $Slic3r::Config->extruder_clearance_radius / 2, 1, JT_ROUND))[0]; } for my $copy (@{$self->copies->[$obj_idx]}) { my $copy_clearance = $clearance->clone; @@ -101,7 +145,7 @@ sub validate { { my @obj_copies = $self->object_copies; pop @obj_copies; # ignore the last copy: its height doesn't matter - if (grep { +($self->objects->[$_->[0]]->mesh->size)[Z] > scale $Slic3r::extruder_clearance_height } @obj_copies) { + if (grep { +($self->objects->[$_->[0]]->mesh->size)[Z] > scale $Slic3r::Config->extruder_clearance_height } @obj_copies) { die "Some objects are too tall and cannot be printed without extruder collisions.\n"; } } @@ -137,26 +181,26 @@ sub layer_count { sub duplicate { my $self = shift; - if ($Slic3r::duplicate_grid->[X] > 1 || $Slic3r::duplicate_grid->[Y] > 1) { + if ($Slic3r::Config->duplicate_grid->[X] > 1 || $Slic3r::Config->duplicate_grid->[Y] > 1) { if (@{$self->objects} > 1) { die "Grid duplication is not supported with multiple objects\n"; } my $object = $self->objects->[0]; # generate offsets for copies - my $dist = scale $Slic3r::duplicate_distance; + my $dist = scale $Slic3r::Config->duplicate_distance; @{$self->copies->[0]} = (); - for my $x_copy (1..$Slic3r::duplicate_grid->[X]) { - for my $y_copy (1..$Slic3r::duplicate_grid->[Y]) { + for my $x_copy (1..$Slic3r::Config->duplicate_grid->[X]) { + for my $y_copy (1..$Slic3r::Config->duplicate_grid->[Y]) { push @{$self->copies->[0]}, [ ($object->x_length + $dist) * ($x_copy-1), ($object->y_length + $dist) * ($y_copy-1), ]; } } - } elsif ($Slic3r::duplicate > 1) { + } elsif ($Slic3r::Config->duplicate > 1) { foreach my $copies (@{$self->copies}) { - @$copies = map [0,0], 1..$Slic3r::duplicate; + @$copies = map [0,0], 1..$Slic3r::Config->duplicate; } $self->arrange_objects; } @@ -173,12 +217,12 @@ sub arrange_objects { } # object distance is max(duplicate_distance, clearance_radius) - my $distance = $Slic3r::complete_objects && $Slic3r::extruder_clearance_radius > $Slic3r::duplicate_distance - ? $Slic3r::extruder_clearance_radius - : $Slic3r::duplicate_distance; + my $distance = $Slic3r::Config->complete_objects && $Slic3r::Config->extruder_clearance_radius > $Slic3r::Config->duplicate_distance + ? $Slic3r::Config->extruder_clearance_radius + : $Slic3r::Config->duplicate_distance; my @positions = Slic3r::Geometry::arrange - ($total_parts, $partx, $party, (map scale $_, @$Slic3r::bed_size), scale $distance); + ($total_parts, $partx, $party, (map scale $_, @$Slic3r::Config->bed_size), scale $distance); for my $obj_idx (0..$#{$self->objects}) { @{$self->copies->[$obj_idx]} = splice @positions, 0, scalar @{$self->copies->[$obj_idx]}; @@ -229,7 +273,7 @@ sub export_gcode { $_->make_perimeters for @{$self->objects}; # simplify slices, we only need the max resolution for perimeters - $_->simplify(scale $Slic3r::resolution) + $_->simplify(scale &Slic3r::RESOLUTION) for map @{$_->expolygon}, map @{$_->slices}, map @{$_->layers}, @{$self->objects}; # this will clip $layer->surfaces to the infill boundaries @@ -297,7 +341,7 @@ sub export_gcode { } # generate support material - if ($Slic3r::support_material) { + if ($Slic3r::Config->support_material) { $status_cb->(85, "Generating support material"); $_->generate_support_material(print => $self) for @{$self->objects}; } @@ -316,10 +360,10 @@ sub export_gcode { $self->write_gcode($output_file); # run post-processing scripts - if (@$Slic3r::post_process) { + if (@{$Slic3r::Config->post_process}) { $status_cb->(95, "Running post-processing scripts"); - Slic3r::Config->setenv; - for (@$Slic3r::post_process) { + $Slic3r::Config->setenv; + for (@{$Slic3r::Config->post_process}) { Slic3r::debugf " '%s' '%s'\n", $_, $output_file; system($_, $output_file); } @@ -388,7 +432,7 @@ EOF } } # generate support material - if ($Slic3r::support_material && $layer_id > 0) { + if ($Slic3r::Config->support_material && $layer_id > 0) { my (@supported_slices, @unsupported_slices) = (); foreach my $expolygon (@current_layer_slices) { my $intersection = intersection_ex( @@ -420,10 +464,10 @@ EOF sub make_skirt { my $self = shift; - return unless $Slic3r::skirts > 0; + return unless $Slic3r::Config->skirts > 0; # collect points from all layers contained in skirt height - my $skirt_height = $Slic3r::skirt_height; + my $skirt_height = $Slic3r::Config->skirt_height; $skirt_height = $self->layer_count if $skirt_height > $self->layer_count; my @points = (); foreach my $obj_idx (0 .. $#{$self->objects}) { @@ -443,9 +487,9 @@ sub make_skirt { # draw outlines from outside to inside my $flow = $Slic3r::first_layer_flow || $Slic3r::flow; my @skirt = (); - for (my $i = $Slic3r::skirts; $i > 0; $i--) { - my $distance = scale ($Slic3r::skirt_distance + ($flow->spacing * $i)); - my $outline = offset([$convex_hull], $distance, $Slic3r::scaling_factor * 100, JT_ROUND); + for (my $i = $Slic3r::Config->skirts; $i > 0; $i--) { + my $distance = scale ($Slic3r::Config->skirt_distance + ($flow->spacing * $i)); + my $outline = offset([$convex_hull], $distance, &Slic3r::SCALING_FACTOR * 100, JT_ROUND); push @skirt, Slic3r::ExtrusionLoop->pack( polygon => Slic3r::Polygon->new(@{$outline->[0]}), role => EXTR_ROLE_SKIRT, @@ -456,7 +500,7 @@ sub make_skirt { sub make_brim { my $self = shift; - return unless $Slic3r::brim_width > 0; + return unless $Slic3r::Config->brim_width > 0; my @islands = (); # array of polygons foreach my $obj_idx (0 .. $#{$self->objects}) { @@ -467,7 +511,7 @@ sub make_brim { } my $flow = $Slic3r::first_layer_flow || $Slic3r::flow; - my $num_loops = sprintf "%.0f", $Slic3r::brim_width / $flow->width; + my $num_loops = sprintf "%.0f", $Slic3r::Config->brim_width / $flow->width; for my $i (reverse 1 .. $num_loops) { push @{$self->brim}, Slic3r::ExtrusionLoop->pack( polygon => Slic3r::Polygon->new($_), @@ -489,14 +533,14 @@ sub write_gcode { printf $fh "; generated by Slic3r $Slic3r::VERSION on %04d-%02d-%02d at %02d:%02d:%02d\n\n", $lt[5] + 1900, $lt[4]+1, $lt[3], $lt[2], $lt[1], $lt[0]; - print $fh "; $_\n" foreach split /\R/, $Slic3r::notes; - print $fh "\n" if $Slic3r::notes; + print $fh "; $_\n" foreach split /\R/, $Slic3r::Config->notes; + print $fh "\n" if $Slic3r::Config->notes; for (qw(layer_height perimeters solid_layers fill_density perimeter_speed infill_speed travel_speed scale)) { - printf $fh "; %s = %s\n", $_, Slic3r::Config->get($_); + printf $fh "; %s = %s\n", $_, $Slic3r::Config->$_; } for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) { - printf $fh "; %s = %s\n", $_, Slic3r::Config->get($_)->[0]; + printf $fh "; %s = %s\n", $_, $Slic3r::Config->$_->[0]; } printf $fh "; single wall width = %.2fmm\n", $Slic3r::flow->width; printf $fh "; first layer single wall width = %.2fmm\n", $Slic3r::first_layer_flow->width @@ -505,29 +549,29 @@ sub write_gcode { # set up our extruder object my $gcodegen = Slic3r::GCode->new; - my $min_print_speed = 60 * $Slic3r::min_print_speed; + my $min_print_speed = 60 * $Slic3r::Config->min_print_speed; my $dec = $gcodegen->dec; print $fh $gcodegen->set_tool(0) if @$Slic3r::extruders > 1; - print $fh $gcodegen->set_fan(0, 1) if $Slic3r::cooling && $Slic3r::disable_fan_first_layers; + print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers; # write start commands to file - printf $fh $gcodegen->set_bed_temperature($Slic3r::first_layer_bed_temperature, 1), - if $Slic3r::first_layer_bed_temperature && $Slic3r::start_gcode !~ /M190/i; - for my $t (grep $Slic3r::extruders->[$_], 0 .. $#$Slic3r::first_layer_temperature) { + printf $fh $gcodegen->set_bed_temperature($Slic3r::Config->first_layer_bed_temperature, 1), + if $Slic3r::Config->first_layer_bed_temperature && $Slic3r::Config->start_gcode !~ /M190/i; + for my $t (grep $Slic3r::extruders->[$_], 0 .. $#{$Slic3r::Config->first_layer_temperature}) { printf $fh $gcodegen->set_temperature($Slic3r::extruders->[$t]->first_layer_temperature, 0, $t) if $Slic3r::extruders->[$t]->first_layer_temperature; } - printf $fh "%s\n", Slic3r::Config->replace_options($Slic3r::start_gcode); - for my $t (grep $Slic3r::extruders->[$_], 0 .. $#$Slic3r::first_layer_temperature) { + printf $fh "%s\n", $Slic3r::Config->replace_options($Slic3r::Config->start_gcode); + for my $t (grep $Slic3r::extruders->[$_], 0 .. $#{$Slic3r::Config->first_layer_temperature}) { printf $fh $gcodegen->set_temperature($Slic3r::extruders->[$t]->first_layer_temperature, 1, $t) - if $Slic3r::extruders->[$t]->first_layer_temperature && $Slic3r::start_gcode !~ /M109/i; + if $Slic3r::extruders->[$t]->first_layer_temperature && $Slic3r::Config->start_gcode !~ /M109/i; } print $fh "G90 ; use absolute coordinates\n"; print $fh "G21 ; set units to millimeters\n"; - if ($Slic3r::gcode_flavor =~ /^(?:reprap|teacup)$/) { + if ($Slic3r::Config->gcode_flavor =~ /^(?:reprap|teacup)$/) { printf $fh $gcodegen->reset_e; - if ($Slic3r::gcode_flavor =~ /^(?:reprap|makerbot)$/) { - if ($Slic3r::use_relative_e_distances) { + if ($Slic3r::Config->gcode_flavor =~ /^(?:reprap|makerbot)$/) { + if ($Slic3r::Config->use_relative_e_distances) { print $fh "M83 ; use relative distances for extrusion\n"; } else { print $fh "M82 ; use absolute distances for extrusion\n"; @@ -538,8 +582,8 @@ sub write_gcode { # calculate X,Y shift to center print around specified origin my @print_bb = $self->bounding_box; my @shift = ( - $Slic3r::print_center->[X] - (unscale ($print_bb[X2] - $print_bb[X1]) / 2) - unscale $print_bb[X1], - $Slic3r::print_center->[Y] - (unscale ($print_bb[Y2] - $print_bb[Y1]) / 2) - unscale $print_bb[Y1], + $Slic3r::Config->print_center->[X] - (unscale ($print_bb[X2] - $print_bb[X1]) / 2) - unscale $print_bb[X1], + $Slic3r::Config->print_center->[Y] - (unscale ($print_bb[Y2] - $print_bb[Y1]) / 2) - unscale $print_bb[Y1], ); # prepare the logic to print one layer @@ -550,12 +594,12 @@ sub write_gcode { my $gcode = ""; if ($layer_id == 1) { - for my $t (grep $Slic3r::extruders->[$_], 0 .. $#$Slic3r::temperature) { + for my $t (grep $Slic3r::extruders->[$_], 0 .. $#{$Slic3r::Config->temperature}) { $gcode .= $gcodegen->set_temperature($Slic3r::extruders->[$t]->temperature, 0, $t) if $Slic3r::extruders->[$t]->temperature && $Slic3r::extruders->[$t]->temperature != $Slic3r::extruders->[$t]->first_layer_temperature; } - $gcode .= $gcodegen->set_bed_temperature($Slic3r::bed_temperature) - if $Slic3r::first_layer_bed_temperature && $Slic3r::bed_temperature != $Slic3r::first_layer_bed_temperature; + $gcode .= $gcodegen->set_bed_temperature($Slic3r::Config->bed_temperature) + if $Slic3r::Config->first_layer_bed_temperature && $Slic3r::Config->bed_temperature != $Slic3r::Config->first_layer_bed_temperature; } # go to layer (just use the first one, we only need Z from it) @@ -563,12 +607,12 @@ sub write_gcode { $gcodegen->elapsed_time(0); # extrude skirt - if ($skirt_done < $Slic3r::skirt_height) { + if ($skirt_done < $Slic3r::Config->skirt_height) { $gcodegen->shift_x($shift[X]); $gcodegen->shift_y($shift[Y]); - $gcode .= $gcodegen->set_acceleration($Slic3r::perimeter_acceleration); + $gcode .= $gcodegen->set_acceleration($Slic3r::Config->perimeter_acceleration); # skip skirt if we have a large brim - if ($layer_id < $Slic3r::skirt_height && ($layer_id != 0 || $Slic3r::skirt_distance + ($Slic3r::skirts * $Slic3r::flow->width) > $Slic3r::brim_width)) { + if ($layer_id < $Slic3r::Config->skirt_height && ($layer_id != 0 || $Slic3r::Config->skirt_distance + ($Slic3r::Config->skirts * $Slic3r::flow->width) > $Slic3r::Config->brim_width)) { $gcode .= $gcodegen->extrude_loop($_, 'skirt') for @{$self->skirt}; } $skirt_done++; @@ -592,12 +636,12 @@ sub write_gcode { $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]); # extrude perimeters - $gcode .= $gcodegen->set_tool($Slic3r::perimeter_extruder-1); + $gcode .= $gcodegen->set_tool($Slic3r::Config->perimeter_extruder-1); $gcode .= $gcodegen->extrude($_, 'perimeter') for @{ $layer->perimeters }; # extrude fills - $gcode .= $gcodegen->set_tool($Slic3r::infill_extruder-1); - $gcode .= $gcodegen->set_acceleration($Slic3r::infill_acceleration); + $gcode .= $gcodegen->set_tool($Slic3r::Config->infill_extruder-1); + $gcode .= $gcodegen->set_acceleration($Slic3r::Config->infill_acceleration); for my $fill (@{ $layer->fills }) { if ($fill->isa('Slic3r::ExtrusionPath::Collection')) { $gcode .= $gcodegen->extrude($_, 'fill') @@ -609,25 +653,25 @@ sub write_gcode { # extrude support material if ($layer->support_fills) { - $gcode .= $gcodegen->set_tool($Slic3r::support_material_extruder-1); + $gcode .= $gcodegen->set_tool($Slic3r::Config->support_material_extruder-1); $gcode .= $gcodegen->extrude_path($_, 'support material') for $layer->support_fills->shortest_path($gcodegen->last_pos); } } return if !$gcode; - my $fan_speed = $Slic3r::fan_always_on ? $Slic3r::min_fan_speed : 0; + my $fan_speed = $Slic3r::Config->fan_always_on ? $Slic3r::Config->min_fan_speed : 0; my $speed_factor = 1; - if ($Slic3r::cooling) { + if ($Slic3r::Config->cooling) { my $layer_time = $gcodegen->elapsed_time; Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $layer_id, $layer_time; - if ($layer_time < $Slic3r::slowdown_below_layer_time) { - $fan_speed = $Slic3r::max_fan_speed; - $speed_factor = $layer_time / $Slic3r::slowdown_below_layer_time; - } elsif ($layer_time < $Slic3r::fan_below_layer_time) { - $fan_speed = $Slic3r::max_fan_speed - ($Slic3r::max_fan_speed - $Slic3r::min_fan_speed) - * ($layer_time - $Slic3r::slowdown_below_layer_time) - / ($Slic3r::fan_below_layer_time - $Slic3r::slowdown_below_layer_time); #/ + if ($layer_time < $Slic3r::Config->slowdown_below_layer_time) { + $fan_speed = $Slic3r::Config->max_fan_speed; + $speed_factor = $layer_time / $Slic3r::Config->slowdown_below_layer_time; + } elsif ($layer_time < $Slic3r::Config->fan_below_layer_time) { + $fan_speed = $Slic3r::Config->max_fan_speed - ($Slic3r::Config->max_fan_speed - $Slic3r::Config->min_fan_speed) + * ($layer_time - $Slic3r::Config->slowdown_below_layer_time) + / ($Slic3r::Config->fan_below_layer_time - $Slic3r::Config->slowdown_below_layer_time); #/ } Slic3r::debugf " fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100; @@ -637,15 +681,15 @@ sub write_gcode { $1 . sprintf("%.${dec}f", $new_speed < $min_print_speed ? $min_print_speed : $new_speed) /gexm; } - $fan_speed = 0 if $layer_id < $Slic3r::disable_fan_first_layers; + $fan_speed = 0 if $layer_id < $Slic3r::Config->disable_fan_first_layers; } $gcode = $gcodegen->set_fan($fan_speed) . $gcode; # bridge fan speed - if (!$Slic3r::cooling || $Slic3r::bridge_fan_speed == 0 || $layer_id < $Slic3r::disable_fan_first_layers) { + if (!$Slic3r::Config->cooling || $Slic3r::Config->bridge_fan_speed == 0 || $layer_id < $Slic3r::Config->disable_fan_first_layers) { $gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm; } else { - $gcode =~ s/^;_BRIDGE_FAN_START\n/ $gcodegen->set_fan($Slic3r::bridge_fan_speed, 1) /gmex; + $gcode =~ s/^;_BRIDGE_FAN_START\n/ $gcodegen->set_fan($Slic3r::Config->bridge_fan_speed, 1) /gmex; $gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex; } @@ -653,7 +697,7 @@ sub write_gcode { }; # do all objects for each layer - if ($Slic3r::complete_objects) { + if ($Slic3r::Config->complete_objects) { # print objects from the smallest to the tallest to avoid collisions # when moving onto next object starting point @@ -677,10 +721,10 @@ sub write_gcode { # another one, set first layer temperatures. this happens before the Z move # is triggered, so machine has more time to reach such temperatures if ($layer_id == 0 && $finished_objects > 0) { - printf $fh $gcodegen->set_bed_temperature($Slic3r::first_layer_bed_temperature), - if $Slic3r::first_layer_bed_temperature; - printf $fh $gcodegen->set_temperature($Slic3r::first_layer_temperature) - if $Slic3r::first_layer_temperature; + printf $fh $gcodegen->set_bed_temperature($Slic3r::Config->first_layer_bed_temperature), + if $Slic3r::Config->first_layer_bed_temperature; + printf $fh $gcodegen->set_temperature($Slic3r::Config->first_layer_temperature) + if $Slic3r::Config->first_layer_temperature; } print $fh $extrude_layer->($layer_id, [[ $obj_idx, $copy ]]); } @@ -703,8 +747,8 @@ sub write_gcode { # write end commands to file print $fh $gcodegen->retract; print $fh $gcodegen->set_fan(0); - print $fh "M501 ; reset acceleration\n" if $Slic3r::acceleration; - printf $fh "%s\n", Slic3r::Config->replace_options($Slic3r::end_gcode); + print $fh "M501 ; reset acceleration\n" if $Slic3r::Config->acceleration; + printf $fh "%s\n", $Slic3r::Config->replace_options($Slic3r::Config->end_gcode); printf $fh "; filament used = %.1fmm (%.1fcm3)\n", $self->total_extrusion_length, $self->total_extrusion_volume; @@ -727,12 +771,12 @@ sub expanded_output_filepath { # if no explicit output file was defined, we take the input # file directory and append the specified filename format my $input_file = $self->objects->[0]->input_file; - $path ||= (fileparse($input_file))[1] . $Slic3r::output_filename_format; + $path ||= (fileparse($input_file))[1] . $Slic3r::Config->output_filename_format; my $input_filename = my $input_filename_base = basename($input_file); $input_filename_base =~ s/\.(?:stl|amf(?:\.xml)?)$//i; - return Slic3r::Config->replace_options($path, { + return $Slic3r::Config->replace_options($path, { input_filename => $input_filename, input_filename_base => $input_filename_base, }); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 6d276d4796..c06358b835 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -170,18 +170,18 @@ sub make_perimeters { # one additional inner perimeter, like the top of domed objects- # this algorithm makes sure that almost one perimeter is overlapping - if ($Slic3r::extra_perimeters && $Slic3r::perimeters > 0) { + if ($Slic3r::Config->extra_perimeters && $Slic3r::Config->perimeters > 0) { for my $layer_id (0 .. $self->layer_count-2) { my $layer = $self->layers->[$layer_id]; my $upper_layer = $self->layers->[$layer_id+1]; - my $overlap = $layer->perimeters_flow->spacing; # one perimeter + my $overlap = $layer->perimeter_flow->spacing; # one perimeter # compute polygons representing the thickness of the first external perimeter of # the upper layer slices my $upper = diff_ex( - [ map @$_, map $_->expolygon->offset_ex(+ 0.5 * scale $layer->perimeters_flow->spacing), @{$upper_layer->slices} ], - [ map @$_, map $_->expolygon->offset_ex(- scale($overlap) + (0.5 * scale $layer->perimeters_flow->spacing)), @{$upper_layer->slices} ], + [ map @$_, map $_->expolygon->offset_ex(+ 0.5 * scale $layer->perimeter_flow->spacing), @{$upper_layer->slices} ], + [ map @$_, map $_->expolygon->offset_ex(- scale($overlap) + (0.5 * scale $layer->perimeter_flow->spacing)), @{$upper_layer->slices} ], ); next if !@$upper; @@ -190,22 +190,22 @@ sub make_perimeters { my $ignore = []; { my $diff = diff_ex( - [ map @$_, map $_->expolygon->offset_ex(- ($Slic3r::perimeters-0.5) * scale $layer->perimeters_flow->spacing), @{$layer->slices} ], + [ map @$_, map $_->expolygon->offset_ex(- ($Slic3r::Config->perimeters-0.5) * scale $layer->perimeter_flow->spacing), @{$layer->slices} ], [ map @{$_->expolygon}, @{$upper_layer->slices} ], ); - $ignore = [ map @$_, map $_->offset_ex(scale $layer->perimeters_flow->spacing), @$diff ]; + $ignore = [ map @$_, map $_->offset_ex(scale $layer->perimeter_flow->spacing), @$diff ]; } foreach my $slice (@{$layer->slices}) { - my $hypothetical_perimeter_num = $Slic3r::perimeters + 1; + my $hypothetical_perimeter_num = $Slic3r::Config->perimeters + 1; CYCLE: while (1) { # compute polygons representing the thickness of the hypotetical new internal perimeter # of our slice my $hypothetical_perimeter; { - my $outer = [ map @$_, $slice->expolygon->offset_ex(- ($hypothetical_perimeter_num-1.5) * scale $layer->perimeters_flow->spacing) ]; + my $outer = [ map @$_, $slice->expolygon->offset_ex(- ($hypothetical_perimeter_num-1.5) * scale $layer->perimeter_flow->spacing) ]; last CYCLE if !@$outer; - my $inner = [ map @$_, $slice->expolygon->offset_ex(- ($hypothetical_perimeter_num-0.5) * scale $layer->perimeters_flow->spacing) ]; + my $inner = [ map @$_, $slice->expolygon->offset_ex(- ($hypothetical_perimeter_num-0.5) * scale $layer->perimeter_flow->spacing) ]; last CYCLE if !@$inner; $hypothetical_perimeter = diff_ex($outer, $inner); } @@ -317,14 +317,14 @@ sub discover_horizontal_shells { foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM) { # find surfaces of current type for current layer # and offset them to take perimeters into account - my @surfaces = map $_->offset($Slic3r::perimeters * scale $layer->perimeters_flow->width), + my @surfaces = map $_->offset($Slic3r::Config->perimeters * scale $layer->perimeter_flow->width), grep $_->surface_type == $type, @{$layer->fill_surfaces} or next; my $surfaces_p = [ map $_->p, @surfaces ]; Slic3r::debugf "Layer %d has %d surfaces of type '%s'\n", $i, scalar(@surfaces), ($type == S_TYPE_TOP ? 'top' : 'bottom'); for (my $n = $type == S_TYPE_TOP ? $i-1 : $i+1; - abs($n - $i) <= $Slic3r::solid_layers-1; + abs($n - $i) <= $Slic3r::Config->solid_layers-1; $type == S_TYPE_TOP ? $n-- : $n++) { next if $n < 0 || $n >= $self->layer_count; @@ -394,7 +394,7 @@ sub discover_horizontal_shells { # combine fill surfaces across layers sub combine_infill { my $self = shift; - return unless $Slic3r::infill_every_layers > 1 && $Slic3r::fill_density > 0; + return unless $Slic3r::Config->infill_every_layers > 1 && $Slic3r::Config->fill_density > 0; my $area_threshold = scale($Slic3r::flow->spacing) ** 2; @@ -407,7 +407,7 @@ sub combine_infill { # for each possible depth, look for intersections with the lower layer # we do this from the greater depth to the smaller - for (my $d = $Slic3r::infill_every_layers - 1; $d >= 1; $d--) { + for (my $d = $Slic3r::Config->infill_every_layers - 1; $d >= 1; $d--) { next if ($i - $d) < 0; my $lower_layer = $self->layer($i - 1); @@ -437,7 +437,7 @@ sub combine_infill { push @new_surfaces, map Slic3r::Surface->new (expolygon => $_, surface_type => S_TYPE_INTERNAL, depth_layers => $d + 1), @$intersection; - foreach my $depth (reverse $d..$Slic3r::infill_every_layers) { + foreach my $depth (reverse $d..$Slic3r::Config->infill_every_layers) { push @new_surfaces, map Slic3r::Surface->new (expolygon => $_, surface_type => S_TYPE_INTERNAL, depth_layers => $depth), @@ -459,7 +459,7 @@ sub combine_infill { { my @new_surfaces = (); push @new_surfaces, grep $_->surface_type != S_TYPE_INTERNAL, @{$lower_layer->fill_surfaces}; - foreach my $depth (1..$Slic3r::infill_every_layers) { + foreach my $depth (1..$Slic3r::Config->infill_every_layers) { push @new_surfaces, map Slic3r::Surface->new (expolygon => $_, surface_type => S_TYPE_INTERNAL, depth_layers => $depth), @@ -486,8 +486,8 @@ sub generate_support_material { my $self = shift; my %params = @_; - my $threshold_rad = deg2rad($Slic3r::support_material_threshold + 1); # +1 makes the threshold inclusive - my $overhang_width = $threshold_rad == 0 ? undef : scale $Slic3r::layer_height * ((cos $threshold_rad) / (sin $threshold_rad)); + my $threshold_rad = deg2rad($Slic3r::Config->support_material_threshold + 1); # +1 makes the threshold inclusive + my $overhang_width = $threshold_rad == 0 ? undef : scale $Slic3r::Config->layer_height * ((cos $threshold_rad) / (sin $threshold_rad)); my $distance_from_object = 1.5 * scale $Slic3r::support_material_flow->width; # determine support regions in each layer (for upper layers) @@ -538,14 +538,14 @@ sub generate_support_material { @{union_ex([ map $_->contour, map @$_, values %layers ])}; my $fill = Slic3r::Fill->new(print => $params{print}); - my $filler = $fill->filler($Slic3r::support_material_pattern); - $filler->angle($Slic3r::support_material_angle); + my $filler = $fill->filler($Slic3r::Config->support_material_pattern); + $filler->angle($Slic3r::Config->support_material_angle); { my @patterns = (); foreach my $expolygon (@support_material_areas) { my @paths = $filler->fill_surface( Slic3r::Surface->new(expolygon => $expolygon), - density => $Slic3r::support_material_flow->spacing / $Slic3r::support_material_spacing, + density => $Slic3r::support_material_flow->spacing / $Slic3r::Config->support_material_spacing, flow_spacing => $Slic3r::support_material_flow->spacing, ); my $params = shift @paths; diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm index f7b41fdcc6..1bfa839f66 100644 --- a/lib/Slic3r/SVG.pm +++ b/lib/Slic3r/SVG.pm @@ -8,12 +8,12 @@ use constant X => 0; use constant Y => 1; sub factor { - return $Slic3r::scaling_factor * 10; + return &Slic3r::SCALING_FACTOR * 10; } sub svg { my ($print) = @_; - $print ||= Slic3r::Print->new(x_length => 200 / $Slic3r::scaling_factor, y_length => 200 / $Slic3r::scaling_factor); + $print ||= Slic3r::Print->new(x_length => 200 / &Slic3r::SCALING_FACTOR, y_length => 200 / &Slic3r::SCALING_FACTOR); my $svg = SVG->new(width => 200 * 10, height => 200 * 10); my $marker_end = $svg->marker( diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index 46652494f8..6f65894501 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -401,9 +401,9 @@ sub slice_facet { } # calculate the layer extents - my $min_layer = int((unscale($min_z) - ($Slic3r::_first_layer_height + $Slic3r::layer_height / 2)) / $Slic3r::layer_height) - 2; + my $min_layer = int((unscale($min_z) - ($Slic3r::Config->get_value('first_layer_height') + $Slic3r::Config->layer_height / 2)) / $Slic3r::Config->layer_height) - 2; $min_layer = 0 if $min_layer < 0; - my $max_layer = int((unscale($max_z) - ($Slic3r::_first_layer_height + $Slic3r::layer_height / 2)) / $Slic3r::layer_height) + 2; + my $max_layer = int((unscale($max_z) - ($Slic3r::Config->get_value('first_layer_height') + $Slic3r::Config->layer_height / 2)) / $Slic3r::Config->layer_height) + 2; Slic3r::debugf "layers: min = %s, max = %s\n", $min_layer, $max_layer; my $lines = {}; # layer_id => [ lines ] diff --git a/slic3r.pl b/slic3r.pl index 54f72c406a..0cf9fab0f3 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -30,66 +30,59 @@ my %cli_options = (); 'export-svg' => \$opt{export_svg}, 'merge|m' => \$opt{merge}, ); - foreach my $opt_key (keys %$Slic3r::Config::Options) { - my $opt = $Slic3r::Config::Options->{$opt_key}; - my $cli = $opt->{cli} or next; - if ($cli =~ /-/) { - # allow alternative options with '_' in place of '-' - $cli = $opt_key.'|'.$cli; - } - $options{ $cli } = \$cli_options{$opt_key}; + foreach my $opt_key (keys %{$Slic3r::Config::Options}) { + my $cli = $Slic3r::Config::Options->{$opt_key}->{cli} or next; + # allow both the dash-separated option name and the full opt_key + $options{ "$opt_key|$cli" } = \$cli_options{$opt_key}; } GetOptions(%options) or usage(1); } -# load configuration +# process command line options +my $cli_config = Slic3r::Config->new_from_cli(%cli_options); + +# load configuration files +my @external_configs = (); if ($opt{load}) { foreach my $configfile (@{$opt{load}}) { if (-e $configfile) { - Slic3r::Config->load($configfile); + push @external_configs, Slic3r::Config->load($configfile); } elsif (-e "$FindBin::Bin/$configfile") { printf STDERR "Loading $FindBin::Bin/$configfile\n"; - Slic3r::Config->load("$FindBin::Bin/$configfile"); + push @external_configs, Slic3r::Config->load("$FindBin::Bin/$configfile"); } else { $opt{ignore_nonexistent_config} or die "Cannot find specified configuration file ($configfile).\n"; } } } -# validate command line options -delete $cli_options{$_} for grep !defined $cli_options{$_}, keys %cli_options; -Slic3r::Config->validate_cli(\%cli_options); +# merge configuration +my $config = Slic3r::Config->new_from_defaults; +$config->apply($_) for @external_configs, $cli_config; -# initialize GUI +# save configuration +if ($opt{save}) { + $config->validate; + $config->save($opt{save}); +} + +# launch GUI my $gui; if (!@ARGV && !$opt{save} && eval "require Slic3r::GUI; 1") { $gui = Slic3r::GUI->new; - $gui->{skeinpanel}->load_config($opt{load}[0]) if $opt{load}; -} -die $@ if $@ && $opt{gui}; - -# apply command line options -Slic3r::Config->set($_ => $cli_options{$_}) for keys %cli_options; - -# validate configuration, convert options like --print-center to arrayrefs, init extruders etc. -# ignore errors if we're launching the GUI -eval { Slic3r::Config->validate }; -die $@ if $@ && !$gui; - -# save configuration -Slic3r::Config->save($opt{save}) if $opt{save}; - -# apply command line options to GUI as well and start it -if ($gui) { - $gui->{skeinpanel}->set_value($_, $cli_options{$_}) for keys %cli_options; + $gui->{skeinpanel}->load_config_file($opt{load}[0]) if $opt{load}; + $gui->{skeinpanel}->load_config($cli_config); $gui->MainLoop; exit; } +die $@ if $@ && $opt{gui}; -if (@ARGV) { +if (@ARGV) { # slicing from command line + $config->validate; + while (my $input_file = shift @ARGV) { - my $print = Slic3r::Print->new; + my $print = Slic3r::Print->new(config => $config); $print->add_object_from_file($input_file); if ($opt{merge}) { $print->add_object_from_file($_) for splice @ARGV, 0; @@ -117,10 +110,12 @@ if (@ARGV) { sub usage { my ($exit_code) = @_; + my $config = Slic3r::Config->new_from_defaults; + my $j = ''; if ($Slic3r::have_threads) { $j = <<"EOF"; - -j, --threads Number of threads to use (1+, default: $Slic3r::threads) + -j, --threads Number of threads to use (1+, default: $config->{threads}) EOF } @@ -143,7 +138,7 @@ $j --output-filename-format Output file name format; all config options enclosed in brackets will be replaced by their values, as well as [input_filename_base] - and [input_filename] (default: $Slic3r::output_filename_format) + and [input_filename] (default: $config->{output_filename_format}) --post-process Generated G-code will be processed with the supplied script; call this more than once to process through multiple scripts. --export-svg Export a SVG file containing slices instead of G-code. @@ -151,13 +146,13 @@ $j print rather than processed individually. Printer options: - --nozzle-diameter Diameter of nozzle in mm (default: @$Slic3r::nozzle_diameter) + --nozzle-diameter Diameter of nozzle in mm (default: $config->{nozzle_diameter}->[0]) --print-center Coordinates in mm of the point to center the print around - (default: $Slic3r::print_center->[0],$Slic3r::print_center->[1]) + (default: $config->{print_center}->[0],$config->{print_center}->[1]) --z-offset Additional height in mm to add to vertical coordinates - (+/-, default: $Slic3r::z_offset) + (+/-, default: $config->{z_offset}) --gcode-flavor The type of G-code to generate (reprap/teacup/makerbot/mach3/no-extrusion, - default: $Slic3r::gcode_flavor) + default: $config->{gcode_flavor}) --use-relative-e-distances Enable this to get relative E values --gcode-arcs Use G2/G3 commands for native arcs (experimental, not supported by all firmwares) @@ -166,50 +161,50 @@ $j --gcode-comments Make G-code verbose by adding comments (default: no) Filament options: - --filament-diameter Diameter in mm of your raw filament (default: @$Slic3r::filament_diameter) + --filament-diameter Diameter in mm of your raw filament (default: $config->{filament_diameter}->[0]) --extrusion-multiplier Change this to alter the amount of plastic extruded. There should be very little need to change this value, which is only useful to - compensate for filament packing (default: @$Slic3r::extrusion_multiplier) - --temperature Extrusion temperature in degree Celsius, set 0 to disable (default: @$Slic3r::temperature) + compensate for filament packing (default: $config->{extrusion_multiplier}->[0]) + --temperature Extrusion temperature in degree Celsius, set 0 to disable (default: $config->{temperature}->[0]) --first-layer-temperature Extrusion temperature for the first layer, in degree Celsius, set 0 to disable (default: same as --temperature) - --bed-temperature Heated bed temperature in degree Celsius, set 0 to disable (default: @$Slic3r::temperature) + --bed-temperature Heated bed temperature in degree Celsius, set 0 to disable (default: $config->{bed_temperature}) --first-layer-bed-temperature Heated bed temperature for the first layer, in degree Celsius, set 0 to disable (default: same as --bed-temperature) Speed options: - --travel-speed Speed of non-print moves in mm/s (default: $Slic3r::travel_speed) - --perimeter-speed Speed of print moves for perimeters in mm/s (default: $Slic3r::perimeter_speed) + --travel-speed Speed of non-print moves in mm/s (default: $config->{travel_speed}) + --perimeter-speed Speed of print moves for perimeters in mm/s (default: $config->{perimeter_speed}) --small-perimeter-speed Speed of print moves for small perimeters in mm/s or % over perimeter speed - (default: $Slic3r::small_perimeter_speed) + (default: $config->{small_perimeter_speed}) --external-perimeter-speed Speed of print moves for the external perimeter in mm/s or % over perimeter speed - (default: $Slic3r::external_perimeter_speed) - --infill-speed Speed of print moves in mm/s (default: $Slic3r::infill_speed) + (default: $config->{external_perimeter_speed}) + --infill-speed Speed of print moves in mm/s (default: $config->{infill_speed}) --solid-infill-speed Speed of print moves for solid surfaces in mm/s or % over infill speed - (default: $Slic3r::solid_infill_speed) + (default: $config->{solid_infill_speed}) --top-solid-infill-speed Speed of print moves for top surfaces in mm/s or % over solid infill speed - (default: $Slic3r::top_solid_infill_speed) - --bridge-speed Speed of bridge print moves in mm/s (default: $Slic3r::bridge_speed) + (default: $config->{top_solid_infill_speed}) + --bridge-speed Speed of bridge print moves in mm/s (default: $config->{bridge_speed}) --first-layer-speed Speed of print moves for bottom layer, expressed either as an absolute - value or as a percentage over normal speeds (default: $Slic3r::first_layer_speed) + value or as a percentage over normal speeds (default: $config->{first_layer_speed}) Accuracy options: - --layer-height Layer height in mm (default: $Slic3r::layer_height) - --first-layer-height Layer height for first layer (mm or %, default: $Slic3r::first_layer_height) + --layer-height Layer height in mm (default: $config->{layer_height}) + --first-layer-height Layer height for first layer (mm or %, default: $config->{first_layer_height}) --infill-every-layers - Infill every N layers (default: $Slic3r::infill_every_layers) + Infill every N layers (default: $config->{infill_every_layers}) Print options: - --perimeters Number of perimeters/horizontal skins (range: 0+, default: $Slic3r::perimeters) + --perimeters Number of perimeters/horizontal skins (range: 0+, default: $config->{perimeters}) --solid-layers Number of solid layers to do for top/bottom surfaces - (range: 1+, default: $Slic3r::solid_layers) - --fill-density Infill density (range: 0-1, default: $Slic3r::fill_density) - --fill-angle Infill angle in degrees (range: 0-90, default: $Slic3r::fill_angle) - --fill-pattern Pattern to use to fill non-solid layers (default: $Slic3r::fill_pattern) - --solid-fill-pattern Pattern to use to fill solid layers (default: $Slic3r::solid_fill_pattern) + (range: 1+, default: $config->{solid_layers}) + --fill-density Infill density (range: 0-1, default: $config->{fill_density}) + --fill-angle Infill angle in degrees (range: 0-90, default: $config->{fill_angle}) + --fill-pattern Pattern to use to fill non-solid layers (default: $config->{fill_pattern}) + --solid-fill-pattern Pattern to use to fill solid layers (default: $config->{solid_fill_pattern}) --start-gcode Load initial G-code from the supplied file. This will overwrite the default command (home all axes [G28]). --end-gcode Load final G-code from the supplied file. This will overwrite @@ -222,62 +217,62 @@ $j Support material options: --support-material Generate support material for overhangs --support-material-threshold - Overhang threshold angle (range: 0-90, default: $Slic3r::support_material_threshold) + Overhang threshold angle (range: 0-90, default: $config->{support_material_threshold}) --support-material-pattern - Pattern to use for support material (default: $Slic3r::support_material_pattern) + Pattern to use for support material (default: $config->{support_material_pattern}) --support-material-spacing - Spacing between pattern lines (mm, default: $Slic3r::support_material_spacing) + Spacing between pattern lines (mm, default: $config->{support_material_spacing}) --support-material-angle - Support material angle in degrees (range: 0-90, default: $Slic3r::support_material_angle) + Support material angle in degrees (range: 0-90, default: $config->{support_material_angle}) Retraction options: --retract-length Length of retraction in mm when pausing extrusion - (default: $Slic3r::retract_length) - --retract-speed Speed for retraction in mm/s (default: $Slic3r::retract_speed) + (default: $config->{retract_length}) + --retract-speed Speed for retraction in mm/s (default: $config->{retract_speed}) --retract-restart-extra Additional amount of filament in mm to push after - compensating retraction (default: $Slic3r::retract_restart_extra) + compensating retraction (default: $config->{retract_restart_extra}) --retract-before-travel - Only retract before travel moves of this length in mm (default: $Slic3r::retract_before_travel) - --retract-lift Lift Z by the given distance in mm when retracting (default: $Slic3r::retract_lift) + Only retract before travel moves of this length in mm (default: $config->{retract_before_travel}) + --retract-lift Lift Z by the given distance in mm when retracting (default: $config->{retract_lift}) Cooling options: --cooling Enable fan and cooling control - --min-fan-speed Minimum fan speed (default: $Slic3r::min_fan_speed%) - --max-fan-speed Maximum fan speed (default: $Slic3r::max_fan_speed%) - --bridge-fan-speed Fan speed to use when bridging (default: $Slic3r::bridge_fan_speed%) + --min-fan-speed Minimum fan speed (default: $config->{min_fan_speed}%) + --max-fan-speed Maximum fan speed (default: $config->{max_fan_speed}%) + --bridge-fan-speed Fan speed to use when bridging (default: $config->{bridge_fan_speed}%) --fan-below-layer-time Enable fan if layer print time is below this approximate number - of seconds (default: $Slic3r::fan_below_layer_time) + of seconds (default: $config->{fan_below_layer_time}) --slowdown-below-layer-time Slow down if layer print time is below this approximate number - of seconds (default: $Slic3r::slowdown_below_layer_time) - --min-print-speed Minimum print speed (mm/s, default: $Slic3r::min_print_speed) - --disable-fan-first-layers Disable fan for the first N layers (default: $Slic3r::disable_fan_first_layers) + of seconds (default: $config->{slowdown_below_layer_time}) + --min-print-speed Minimum print speed (mm/s, default: $config->{min_print_speed}) + --disable-fan-first-layers Disable fan for the first N layers (default: $config->{disable_fan_first_layers}) --fan-always-on Keep fan always on at min fan speed, even for layers that don't need cooling Skirt options: - --skirts Number of skirts to draw (0+, default: $Slic3r::skirts) + --skirts Number of skirts to draw (0+, default: $config->{skirts}) --skirt-distance Distance in mm between innermost skirt and object - (default: $Slic3r::skirt_distance) - --skirt-height Height of skirts to draw (expressed in layers, 0+, default: $Slic3r::skirt_height) + (default: $config->{skirt_distance}) + --skirt-height Height of skirts to draw (expressed in layers, 0+, default: $config->{skirt_height}) --brim-width Width of the brim that will get added to each object to help adhesion - (mm, default: $Slic3r::brim_width) + (mm, default: $config->{brim_width}) Transform options: - --scale Factor for scaling input object (default: $Slic3r::scale) - --rotate Rotation angle in degrees (0-360, default: $Slic3r::rotate) - --duplicate Number of items with auto-arrange (1+, default: $Slic3r::duplicate) - --bed-size Bed size, only used for auto-arrange (mm, default: $Slic3r::bed_size->[0],$Slic3r::bed_size->[1]) - --duplicate-grid Number of items with grid arrangement (default: $Slic3r::duplicate_grid->[0],$Slic3r::duplicate_grid->[1]) - --duplicate-distance Distance in mm between copies (default: $Slic3r::duplicate_distance) + --scale Factor for scaling input object (default: $config->{scale}) + --rotate Rotation angle in degrees (0-360, default: $config->{rotate}) + --duplicate Number of items with auto-arrange (1+, default: $config->{duplicate}) + --bed-size Bed size, only used for auto-arrange (mm, default: $config->{bed_size}->[0],$config->{bed_size}->[1]) + --duplicate-grid Number of items with grid arrangement (default: $config->{duplicate_grid}->[0],$config->{duplicate_grid}->[1]) + --duplicate-distance Distance in mm between copies (default: $config->{duplicate_distance}) Sequential printing options: --complete-objects When printing multiple objects and/or copies, complete each one before starting the next one; watch out for extruder collisions (default: no) --extruder-clearance-radius Radius in mm above which extruder won't collide with anything - (default: $Slic3r::extruder_clearance_radius) + (default: $config->{extruder_clearance_radius}) --extruder-clearance-height Maximum vertical extruder depth; i.e. vertical distance from - extruder tip and carriage bottom (default: $Slic3r::extruder_clearance_height) + extruder tip and carriage bottom (default: $config->{extruder_clearance_height}) Miscellaneous options: --notes Notes to be added as comments to the output file @@ -293,7 +288,7 @@ $j Set a different extrusion width for infill --support-material-extrusion-width Set a different extrusion width for support material - --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: $Slic3r::bridge_flow_ratio) + --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: $config->{bridge_flow_ratio}) Multiple extruder options: --perimeters-extruder