diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 9f5de4f4..21fa2322 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -15,18 +15,18 @@ our $Options = print_config_def(); { no strict 'refs'; for my $opt_key (keys %$Options) { - *{$opt_key} = sub { $_[0]->_get($opt_key) }; + *{$opt_key} = sub { $_[0]->get($opt_key) }; } } -sub _get { - my ($self, $opt_key) = @_; - use XXX; - if (!defined first { $_ eq $opt_key } @{$self->get_keys}) { - ZZZ $opt_key; - } - return $self->get($opt_key); -} +# sub _get { +# my ($self, $opt_key) = @_; +# use XXX; +# if (!defined first { $_ eq $opt_key } @{$self->get_keys}) { +# ZZZ $opt_key; +# } +# return $self->get($opt_key); +# } sub new_from_defaults { my $class = shift; @@ -200,7 +200,8 @@ sub diff { return [@diff]; } -# this method is idempotent by design +# this method is idempotent by design and only applies to ::DynamicConfig or ::Full +# objects because it performs cross checks sub validate { my $self = shift; @@ -317,6 +318,31 @@ sub validate { if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration) && !$self->default_acceleration; + # --spiral-vase + if ($self->spiral_vase) { + # Note that we might want to have more than one perimeter on the bottom + # solid layers. + die "Can't make more than one perimeter when spiral vase mode is enabled\n" + if $self->perimeters > 1; + + die "Can't make less than one perimeter when spiral vase mode is enabled\n" + if $self->perimeters < 1; + + die "Spiral vase mode is not compatible with non-zero fill density\n" + if $self->fill_density > 0; + + die "Spiral vase mode is not compatible with top solid layers\n" + if $self->top_solid_layers > 0; + + die "Spiral vase mode is not compatible with support material\n" + if $self->support_material || $self->support_material_enforce_layers > 0; + + # This should be enforce automatically only on spiral layers and + # done on the others + die "Spiral vase mode is not compatible with retraction on layer change\n" + if defined first { $_ } @{ $self->retract_layer_change }; + } + # general validation, quick and dirty foreach my $opt_key (@{$self->get_keys}) { my $opt = $Options->{$opt_key}; diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 1025c345..9b8b1dc9 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -18,7 +18,7 @@ use constant OPTIONS => [qw( has 'id' => (is => 'rw', required => 1); has $_ => (is => 'ro', required => 1) for @{&OPTIONS}; -has 'config'=> (is => 'ro', required => 1); +has 'use_relative_e_distances' => (is => 'ro', default => sub {0}); has 'E' => (is => 'rw', default => sub {0} ); has 'absolute_E' => (is => 'rw', default => sub {0} ); @@ -56,7 +56,7 @@ sub scaled_wipe_distance { sub extrude { my ($self, $E) = @_; - $self->E(0) if $self->config->use_relative_e_distances; + $self->E(0) if $self->use_relative_e_distances; $self->absolute_E($self->absolute_E + $E); return $self->E($self->E + $E); } diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 42cbea13..7969431c 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -1,6 +1,7 @@ package Slic3r::Fill; use Moo; +use Slic3r::ExtrusionPath ':roles'; use Slic3r::Fill::ArchimedeanChords; use Slic3r::Fill::Base; use Slic3r::Fill::Concentric; @@ -11,7 +12,7 @@ use Slic3r::Fill::Line; use Slic3r::Fill::OctagramSpiral; use Slic3r::Fill::PlanePath; use Slic3r::Fill::Rectilinear; -use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(X Y PI scale chained_path); use Slic3r::Geometry::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2); use Slic3r::Surface ':types'; @@ -50,7 +51,10 @@ sub make_fill { my ($layerm) = @_; Slic3r::debugf "Filling layer %d:\n", $layerm->id; - my $fill_density = $layerm->config->fill_density; + + my $fill_density = $layerm->config->fill_density; + my $infill_flow = $layerm->flow(FLOW_ROLE_INFILL); + my $solid_infill_flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL); my @surfaces = (); @@ -95,7 +99,7 @@ sub make_fill { # we are going to grow such regions by overlapping them with the void (if any) # TODO: detect and investigate whether there could be narrow regions without # any void neighbors - my $distance_between_surfaces = $layerm->solid_infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING; + my $distance_between_surfaces = $infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING; { my $collapsed = diff( [ map @{$_->expolygon}, @surfaces ], @@ -133,11 +137,10 @@ sub make_fill { my $filler = $layerm->config->fill_pattern; my $density = $fill_density; my $flow = ($surface->surface_type == S_TYPE_TOP) - ? $layerm->top_infill_flow + ? $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL) : $surface->is_solid - ? $layerm->solid_infill_flow - : $layerm->infill_flow; - my $flow_spacing = $flow->spacing; + ? $solid_infill_flow + : $infill_flow; my $is_bridge = $layerm->id > 0 && $surface->is_bridge; my $is_solid = $surface->is_solid; @@ -147,7 +150,7 @@ sub make_fill { $filler = $layerm->config->solid_fill_pattern; if ($is_bridge) { $filler = 'rectilinear'; - $flow_spacing = $layerm->extruders->{infill}->bridge_flow->spacing; + $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 1); } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { $filler = 'rectilinear'; } @@ -155,6 +158,8 @@ sub make_fill { next SURFACE unless $density > 0; } + my $flow_spacing = $flow->spacing; + my $f = $self->filler($filler); $f->layer_id($layerm->id); $f->angle($layerm->config->fill_angle); @@ -166,7 +171,7 @@ sub make_fill { next unless @polylines; # ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed) - $params->{flow_spacing} = $layerm->extruders->{infill}->bridge_flow->width if $is_bridge; + $params->{flow_spacing} = $flow->width if $is_bridge; # save into layer push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new; diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 0a9747fa..3930dce5 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -36,7 +36,10 @@ sub BUILDARGS { $args{width} = $args{layer_height} * $1 / 100; } $args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}); - %args = @args{qw(width spacing)}; + %args = ( + width => $args{width}, + spacing => $args{spacing}, + ); } return {%args}; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index da55942f..66175260 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -3,11 +3,12 @@ use Moo; use List::Util qw(min first); use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon points_coincide PI X Y B); use Slic3r::Geometry::Clipper qw(union_ex); use Slic3r::Surface ':types'; -has 'config' => (is => 'ro', required => 1); +has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new }); has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'extruders' => (is => 'ro', required => 1); has 'multiple_extruders' => (is => 'lazy'); @@ -16,6 +17,7 @@ has 'enable_loop_clipping' => (is => 'rw', default => sub {1}); has 'enable_wipe' => (is => 'lazy'); # at least one extruder has wipe enabled has 'layer_count' => (is => 'ro', required => 1 ); has 'layer' => (is => 'rw'); +has 'region' => (is => 'rw'); has '_layer_islands' => (is => 'rw'); has '_upper_layer_islands' => (is => 'rw'); has '_layer_overhangs' => (is => 'ro', default => sub { Slic3r::ExPolygon::Collection->new }); @@ -23,6 +25,8 @@ has 'shift_x' => (is => 'rw', default => sub {0} ); has 'shift_y' => (is => 'rw', default => sub {0} ); has 'z' => (is => 'rw'); has 'speed' => (is => 'rw'); +has '_extrusion_axis' => (is => 'rw'); +has '_retract_lift' => (is => 'rw'); has 'speeds' => (is => 'lazy'); # mm/min has 'external_mp' => (is => 'rw'); @@ -38,12 +42,19 @@ has 'last_f' => (is => 'rw', default => sub {""}); has 'last_fan_speed' => (is => 'rw', default => sub {0}); has 'wipe_path' => (is => 'rw'); +sub BUILD { + my ($self) = @_; + + $self->_extrusion_axis($self->print_config->get_extrusion_axis); + $self->_retract_lift($self->print_config->retract_lift->[0]); +} + sub _build_speeds { my $self = shift; return { - map { $_ => 60 * $self->config->get_value("${_}_speed") } + map { $_ => 60 * $self->print_config->get_value("${_}_speed") } qw(travel perimeter small_perimeter external_perimeter infill - solid_infill top_solid_infill support_material bridge gap_fill retract), + solid_infill top_solid_infill bridge gap_fill retract), }; } @@ -59,7 +70,6 @@ my %role_speeds = ( &EXTR_ROLE_BRIDGE => 'bridge', &EXTR_ROLE_INTERNALBRIDGE => 'solid_infill', &EXTR_ROLE_SKIRT => 'perimeter', - &EXTR_ROLE_SUPPORTMATERIAL => 'support_material', &EXTR_ROLE_GAPFILL => 'gap_fill', ); @@ -97,29 +107,29 @@ sub change_layer { $self->_layer_islands($layer->islands); $self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []); $self->_layer_overhangs->clear; - if ($layer->id > 0 && ($layer->config->overhangs || $self->config->start_perimeters_at_non_overhang)) { + if ($layer->id > 0 && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) { $self->_layer_overhangs->append( # clone ExPolygons because they come from Surface objects but will be used outside here map $_->expolygon, map @{$_->slices->filter_by_type(S_TYPE_BOTTOM)}, @{$layer->regions} ); } - if ($self->config->avoid_crossing_perimeters) { + if ($self->print_config->avoid_crossing_perimeters) { $self->layer_mp(Slic3r::GCode::MotionPlanner->new( islands => union_ex([ map @$_, @{$layer->slices} ], 1), )); } my $gcode = ""; - if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { + if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { $gcode .= sprintf "M73 P%s%s\n", int(99 * ($layer->id / ($self->layer_count - 1))), - ($self->config->gcode_comments ? ' ; update progress' : ''); + ($self->print_config->gcode_comments ? ' ; update progress' : ''); } - if ($self->config->first_layer_acceleration) { + if ($self->print_config->first_layer_acceleration) { if ($layer->id == 0) { - $gcode .= $self->set_acceleration($self->config->first_layer_acceleration); + $gcode .= $self->set_acceleration($self->print_config->first_layer_acceleration); } elsif ($layer->id == 1) { - $gcode .= $self->set_acceleration($self->config->default_acceleration); + $gcode .= $self->set_acceleration($self->print_config->default_acceleration); } } @@ -133,7 +143,7 @@ sub move_z { my $gcode = ""; - $z += $self->config->z_offset; + $z += $self->print_config->z_offset; my $current_z = $self->z; my $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef; @@ -181,11 +191,11 @@ sub extrude_loop { # find candidate starting points # start looking for concave vertices not being overhangs my @concave = (); - if ($self->config->start_perimeters_at_concave_points) { + if ($self->print_config->start_perimeters_at_concave_points) { @concave = $polygon->concave_points; } my @candidates = (); - if ($self->config->start_perimeters_at_non_overhang) { + if ($self->print_config->start_perimeters_at_non_overhang) { @candidates = grep !$self->_layer_overhangs->contains_point($_), @concave; } if (!@candidates) { @@ -193,7 +203,7 @@ sub extrude_loop { @candidates = @concave; if (!@candidates) { # if none, look for any non-overhang vertex - if ($self->config->start_perimeters_at_non_overhang) { + if ($self->print_config->start_perimeters_at_non_overhang) { @candidates = grep !$self->_layer_overhangs->contains_point($_), @$polygon; } if (!@candidates) { @@ -206,9 +216,9 @@ 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 ($self->config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) { - $last_pos = Slic3r::Point->new(scale $self->config->print_center->[X], scale $self->config->bed_size->[Y]); - $last_pos->rotate(rand(2*PI), $self->config->print_center); + if ($self->print_config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) { + $last_pos = Slic3r::Point->new(scale $self->print_config->print_center->[X], scale $self->print_config->bed_size->[Y]); + $last_pos->rotate(rand(2*PI), $self->print_config->print_center); } # split the loop at the starting point and make a path @@ -224,7 +234,7 @@ sub extrude_loop { my @paths = (); # detect overhanging/bridging perimeters - if ($self->layer->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) { + if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) { # get non-overhang paths by subtracting overhangs from the loop push @paths, map $_->clone, @@ -234,7 +244,7 @@ sub extrude_loop { push @paths, map { $_->role(EXTR_ROLE_OVERHANG_PERIMETER); - $_->flow_spacing($self->extruder->bridge_flow->width); + $_->flow_spacing($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->width); $_ } map $_->clone, @@ -259,7 +269,7 @@ sub extrude_loop { $self->wipe_path($extrusion_path->polyline->clone) if $self->enable_wipe; # make a little move inwards before leaving loop - if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->layer->object->config->perimeters > 1) { + if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->region->config->perimeters > 1) { # detect angle between last and first segment # the side depends on the original winding order of the polygon (left for contours, right for holes) my @points = $was_clockwise ? (-2, 1) : (1, -2); @@ -299,13 +309,13 @@ sub extrude_path { # adjust acceleration my $acceleration; - if (!$self->config->first_layer_acceleration || $self->layer->id != 0) { - if ($self->config->perimeter_acceleration && $path->is_perimeter) { - $acceleration = $self->config->perimeter_acceleration; - } elsif ($self->config->infill_acceleration && $path->is_fill) { - $acceleration = $self->config->infill_acceleration; - } elsif ($self->config->infill_acceleration && $path->is_bridge) { - $acceleration = $self->config->bridge_acceleration; + if (!$self->print_config->first_layer_acceleration || $self->layer->id != 0) { + if ($self->print_config->perimeter_acceleration && $path->is_perimeter) { + $acceleration = $self->print_config->perimeter_acceleration; + } elsif ($self->print_config->infill_acceleration && $path->is_fill) { + $acceleration = $self->print_config->infill_acceleration; + } elsif ($self->print_config->infill_acceleration && $path->is_bridge) { + $acceleration = $self->print_config->bridge_acceleration; } $gcode .= $self->set_acceleration($acceleration) if $acceleration; } @@ -322,15 +332,13 @@ sub extrude_path { # calculate extrusion length per distance unit my $e = $self->extruder->e_per_mm3 * $area; - $e = 0 if !$self->config->extrusion_axis; + $e = 0 if !$self->_extrusion_axis; # set speed $self->speed( $params{speed} || $role_speeds{$path->role} || die "Unknown role: " . $path->role ); my $F = $self->speeds->{$self->speed} // $self->speed; if ($self->layer->id == 0) { - $F = $self->config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ - ? sprintf("%.3f", $F * $1/100) - : $self->config->first_layer_speed * 60; + $F = $self->print_config->get_abs_value_over('first_layer_speed', $F/60) * 60; } # extrude arc or line @@ -350,12 +358,12 @@ sub extrude_path { $gcode .= sprintf "G1 X%.3f Y%.3f", ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** - $gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E) + $gcode .= sprintf(" %s%.5f", $self->_extrusion_axis, $E) if $E; $gcode .= " F$local_F" if $local_F; $gcode .= " ; $description" - if $self->config->gcode_comments; + if $self->print_config->gcode_comments; $gcode .= "\n"; # only include F in the first line @@ -369,19 +377,14 @@ sub extrude_path { $gcode .= ";_BRIDGE_FAN_END\n" if $path->is_bridge; $self->last_pos($path->last_point); - if ($self->config->cooling) { + if ($self->print_config->cooling) { my $path_time = $path_length / $F * 60; - if ($self->layer->id == 0) { - $path_time = $self->config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ - ? $path_time / ($1/100) - : $path_length / $self->config->first_layer_speed * 60; - } $self->elapsed_time($self->elapsed_time + $path_time); } # reset acceleration - $gcode .= $self->set_acceleration($self->config->default_acceleration) - if $acceleration && $self->config->default_acceleration; + $gcode .= $self->set_acceleration($self->print_config->default_acceleration) + if $acceleration && $self->print_config->default_acceleration; return $gcode; } @@ -400,7 +403,7 @@ sub travel_to { # skip retraction if the travel move is contained in an island in the current layer # *and* in an island in the upper layer (so that the ooze will not be visible) if ($travel->length < scale $self->extruder->retract_before_travel - || ($self->config->only_retract_when_crossing_perimeters + || ($self->print_config->only_retract_when_crossing_perimeters && (first { $_->contains_line($travel) } @{$self->_upper_layer_islands}) && (first { $_->contains_line($travel) } @{$self->_layer_islands})) || (defined $role && $role == EXTR_ROLE_SUPPORTMATERIAL && (first { $_->contains_line($travel) } @{$self->layer->support_islands})) @@ -408,7 +411,7 @@ sub travel_to { $self->straight_once(0); $self->speed('travel'); $gcode .= $self->G0($point, undef, 0, $comment || ""); - } elsif (!$self->config->avoid_crossing_perimeters || $self->straight_once) { + } elsif (!$self->print_config->avoid_crossing_perimeters || $self->straight_once) { $self->straight_once(0); $gcode .= $self->retract; $self->speed('travel'); @@ -441,7 +444,7 @@ sub _plan { my @travel = @{$mp->shortest_path($self->last_pos, $point)->lines}; # if the path is not contained in a single island we need to retract - my $need_retract = !$self->config->only_retract_when_crossing_perimeters; + my $need_retract = !$self->print_config->only_retract_when_crossing_perimeters; if (!$need_retract) { $need_retract = 1; foreach my $island (@{$self->_upper_layer_islands}) { @@ -481,14 +484,14 @@ sub retract { if ($self->extruder->wipe && $self->wipe_path) { my @points = @{$self->wipe_path}; $wipe_path = Slic3r::Polyline->new($self->last_pos, @{$self->wipe_path}[1..$#{$self->wipe_path}]); - $wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->config->travel_speed)); + $wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->print_config->travel_speed)); } # prepare moves my $retract = [undef, undef, -$length, $comment]; - my $lift = ($self->extruder->retract_lift == 0 || defined $params{move_z}) && !$self->lifted + my $lift = ($self->_retract_lift == 0 || defined $params{move_z}) && !$self->lifted ? undef - : [undef, $self->z + $self->extruder->retract_lift, 0, 'lift plate during travel']; + : [undef, $self->z + $self->_retract_lift, 0, 'lift plate during travel']; # check that we have a positive wipe length if ($wipe_path) { @@ -500,7 +503,7 @@ sub retract { my $segment_length = $line->length; # reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one # due to rounding - my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->config->travel_speed)) * 0.95; + my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->print_config->travel_speed)) * 0.95; $retracted += $e; $gcode .= $self->G1($line->b, undef, $e, $retract->[3] . ";_WIPE"); } @@ -510,7 +513,7 @@ sub retract { $self->speed('retract'); $gcode .= $self->G1(undef, undef, $retract->[2] - $retracted, $comment); } - } elsif ($self->config->use_firmware_retraction) { + } elsif ($self->print_config->use_firmware_retraction) { $gcode .= "G10 ; retract\n"; } else { $self->speed('retract'); @@ -518,23 +521,23 @@ sub retract { } if (!$self->lifted) { $self->speed('travel'); - if (defined $params{move_z} && $self->extruder->retract_lift > 0) { - my $travel = [undef, $params{move_z} + $self->extruder->retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift']; + if (defined $params{move_z} && $self->_retract_lift > 0) { + my $travel = [undef, $params{move_z} + $self->_retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift']; $gcode .= $self->G0(@$travel); - $self->lifted($self->extruder->retract_lift); + $self->lifted($self->_retract_lift); } elsif ($lift) { $gcode .= $self->G1(@$lift); } } $self->extruder->retracted($self->extruder->retracted + $length); $self->extruder->restart_extra($restart_extra); - $self->lifted($self->extruder->retract_lift) if $lift; + $self->lifted($self->_retract_lift) if $lift; # reset extrusion distance during retracts # this makes sure we leave sufficient precision in the firmware $gcode .= $self->reset_e; - $gcode .= "M103 ; extruder off\n" if $self->config->gcode_flavor eq 'makerware'; + $gcode .= "M103 ; extruder off\n" if $self->print_config->gcode_flavor eq 'makerware'; return $gcode; } @@ -543,7 +546,7 @@ sub unretract { my ($self) = @_; my $gcode = ""; - $gcode .= "M101 ; extruder on\n" if $self->config->gcode_flavor eq 'makerware'; + $gcode .= "M101 ; extruder on\n" if $self->print_config->gcode_flavor eq 'makerware'; if ($self->lifted) { $self->speed('travel'); @@ -554,15 +557,15 @@ sub unretract { my $to_unretract = $self->extruder->retracted + $self->extruder->restart_extra; if ($to_unretract) { $self->speed('retract'); - if ($self->config->use_firmware_retraction) { + if ($self->print_config->use_firmware_retraction) { $gcode .= "G11 ; unretract\n"; - } elsif ($self->config->extrusion_axis) { + } elsif ($self->_extrusion_axis) { # use G1 instead of G0 because G0 will blend the restart with the previous travel move $gcode .= sprintf "G1 %s%.5f F%.3f", - $self->config->extrusion_axis, + $self->_extrusion_axis, $self->extruder->extrude($to_unretract), $self->extruder->retract_speed_mm_min; - $gcode .= " ; compensate retraction" if $self->config->gcode_comments; + $gcode .= " ; compensate retraction" if $self->print_config->gcode_comments; $gcode .= "\n"; } $self->extruder->retracted(0); @@ -574,11 +577,11 @@ sub unretract { sub reset_e { my ($self) = @_; - return "" if $self->config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/; + return "" if $self->print_config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/; $self->extruder->E(0) if $self->extruder; - return sprintf "G92 %s0%s\n", $self->config->extrusion_axis, ($self->config->gcode_comments ? ' ; reset extrusion distance' : '') - if $self->config->extrusion_axis && !$self->config->use_relative_e_distances; + return sprintf "G92 %s0%s\n", $self->_extrusion_axis, ($self->print_config->gcode_comments ? ' ; reset extrusion distance' : '') + if $self->_extrusion_axis && !$self->print_config->use_relative_e_distances; } sub set_acceleration { @@ -586,12 +589,12 @@ sub set_acceleration { return "" if !$acceleration; return sprintf "M204 S%s%s\n", - $acceleration, ($self->config->gcode_comments ? ' ; adjust acceleration' : ''); + $acceleration, ($self->print_config->gcode_comments ? ' ; adjust acceleration' : ''); } sub G0 { my $self = shift; - return $self->G1(@_) if !($self->config->g0 || $self->config->gcode_flavor eq 'mach3'); + return $self->G1(@_) if !($self->print_config->g0 || $self->print_config->gcode_flavor eq 'mach3'); return $self->_G0_G1("G0", @_); } @@ -628,11 +631,11 @@ sub _Gx { $gcode .= sprintf " F%.3f", $F; # output extrusion distance - if ($e && $self->config->extrusion_axis) { - $gcode .= sprintf " %s%.5f", $self->config->extrusion_axis, $self->extruder->extrude($e); + if ($e && $self->_extrusion_axis) { + $gcode .= sprintf " %s%.5f", $self->_extrusion_axis, $self->extruder->extrude($e); } - $gcode .= " ; $comment" if $comment && $self->config->gcode_comments; + $gcode .= " ; $comment" if $comment && $self->print_config->gcode_comments; return "$gcode\n"; } @@ -653,8 +656,8 @@ sub set_extruder { $gcode .= $self->retract(toolchange => 1) if defined $self->extruder; # append custom toolchange G-code - if (defined $self->extruder && $self->config->toolchange_gcode) { - $gcode .= sprintf "%s\n", $self->replace_variables($self->config->toolchange_gcode, { + if (defined $self->extruder && $self->print_config->toolchange_gcode) { + $gcode .= sprintf "%s\n", $self->replace_variables($self->print_config->toolchange_gcode, { previous_extruder => $self->extruder->id, next_extruder => $extruder->id, }); @@ -669,24 +672,24 @@ sub set_extruder { ? $self->extruder->first_layer_temperature : $self->extruder->temperature; # we assume that heating is always slower than cooling, so no need to block - $gcode .= $self->set_temperature($temp + $self->config->standby_temperature_delta, 0); + $gcode .= $self->set_temperature($temp + $self->print_config->standby_temperature_delta, 0); } # set the new extruder $self->extruder($extruder); $gcode .= sprintf "%s%d%s\n", - ($self->config->gcode_flavor eq 'makerware' + ($self->print_config->gcode_flavor eq 'makerware' ? 'M135 T' - : $self->config->gcode_flavor eq 'sailfish' + : $self->print_config->gcode_flavor eq 'sailfish' ? 'M108 T' : 'T'), $extruder->id, - ($self->config->gcode_comments ? ' ; change extruder' : ''); + ($self->print_config->gcode_comments ? ' ; change extruder' : ''); $gcode .= $self->reset_e; # set the new extruder to the operating temperature - if ($self->config->ooze_prevention) { + if ($self->print_config->ooze_prevention) { my $temp = defined $self->layer && $self->layer->id == 0 ? $self->extruder->first_layer_temperature : $self->extruder->temperature; @@ -702,18 +705,18 @@ sub set_fan { if ($self->last_fan_speed != $speed || $dont_save) { $self->last_fan_speed($speed) if !$dont_save; if ($speed == 0) { - my $code = $self->config->gcode_flavor eq 'teacup' + my $code = $self->print_config->gcode_flavor eq 'teacup' ? 'M106 S0' - : $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ + : $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M127' : 'M107'; - return sprintf "$code%s\n", ($self->config->gcode_comments ? ' ; disable fan' : ''); + return sprintf "$code%s\n", ($self->print_config->gcode_comments ? ' ; disable fan' : ''); } else { - if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { - return sprintf "M126%s\n", ($self->config->gcode_comments ? ' ; enable fan' : ''); + if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { + return sprintf "M126%s\n", ($self->print_config->gcode_comments ? ' ; enable fan' : ''); } else { - return sprintf "M106 %s%d%s\n", ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), - (255 * $speed / 100), ($self->config->gcode_comments ? ' ; enable fan' : ''); + return sprintf "M106 %s%d%s\n", ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), + (255 * $speed / 100), ($self->print_config->gcode_comments ? ' ; enable fan' : ''); } } } @@ -723,17 +726,17 @@ sub set_fan { sub set_temperature { my ($self, $temperature, $wait, $tool) = @_; - return "" if $wait && $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/; + return "" if $wait && $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/; - my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup') + my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup') ? ('M109', 'wait for temperature to be reached') : ('M104', 'set temperature'); my $gcode = sprintf "$code %s%d %s; $comment\n", - ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature, - (defined $tool && ($self->multiple_extruders || $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : ""; + ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature, + (defined $tool && ($self->multiple_extruders || $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : ""; $gcode .= "M116 ; wait for temperature to be reached\n" - if $self->config->gcode_flavor eq 'teacup' && $wait; + if $self->print_config->gcode_flavor eq 'teacup' && $wait; return $gcode; } @@ -741,21 +744,21 @@ sub set_temperature { sub set_bed_temperature { my ($self, $temperature, $wait) = @_; - my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup') - ? (($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached') + my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup') + ? (($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached') : ('M140', 'set bed temperature'); my $gcode = sprintf "$code %s%d ; $comment\n", - ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; + ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; $gcode .= "M116 ; wait for bed temperature to be reached\n" - if $self->config->gcode_flavor eq 'teacup' && $wait; + if $self->print_config->gcode_flavor eq 'teacup' && $wait; return $gcode; } sub replace_variables { my ($self, $string, $extra) = @_; - return $self->config->replace_options($string, { %{$self->extra_variables}, %{ $extra || {} } }); + return $self->print_config->replace_options($string, { %{$self->extra_variables}, %{ $extra || {} } }); } 1; diff --git a/lib/Slic3r/GCode/ArcFitting.pm b/lib/Slic3r/GCode/ArcFitting.pm index 52078bdc..fe45bdb3 100644 --- a/lib/Slic3r/GCode/ArcFitting.pm +++ b/lib/Slic3r/GCode/ArcFitting.pm @@ -104,7 +104,7 @@ sub flush_path { $gcode .= sprintf " I%.3f J%.3f", map { unscale($arc_center->[$_] - $cur_path->[0][$_]) } (X,Y); my $E = 0; # TODO: compute E using $length - $gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E) + $gcode .= sprintf(" %s%.5f", $self->config->get_extrusion_axis, $E) if $E; my $F = 0; # TODO: extract F from original moves diff --git a/lib/Slic3r/GCode/CoolingBuffer.pm b/lib/Slic3r/GCode/CoolingBuffer.pm index d95b5fec..91119fe3 100644 --- a/lib/Slic3r/GCode/CoolingBuffer.pm +++ b/lib/Slic3r/GCode/CoolingBuffer.pm @@ -1,7 +1,7 @@ package Slic3r::GCode::CoolingBuffer; use Moo; -has 'config' => (is => 'ro', required => 1); +has 'config' => (is => 'ro', required => 1); # Slic3r::Config::Print has 'gcodegen' => (is => 'ro', required => 1); has 'gcode' => (is => 'rw', default => sub {""}); has 'elapsed_time' => (is => 'rw', default => sub {0}); diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm index f888ddac..f64b6a1b 100644 --- a/lib/Slic3r/GCode/Layer.pm +++ b/lib/Slic3r/GCode/Layer.pm @@ -19,24 +19,24 @@ has '_last_obj_copy' => (is => 'rw'); sub _build_spiralvase { my $self = shift; - return $self->gcodegen->config->spiral_vase - ? Slic3r::GCode::SpiralVase->new(config => $self->gcodegen->config) + return $self->print->config->spiral_vase + ? Slic3r::GCode::SpiralVase->new(config => $self->print->config) : undef; } sub _build_vibration_limit { my $self = shift; - return $self->gcodegen->config->vibration_limit - ? Slic3r::GCode::VibrationLimit->new(config => $self->gcodegen->config) + return $self->print->config->vibration_limit + ? Slic3r::GCode::VibrationLimit->new(config => $self->print->config) : undef; } sub _build_arc_fitting { my $self = shift; - return $self->gcodegen->config->gcode_arcs - ? Slic3r::GCode::ArcFitting->new(config => $self->gcodegen->config) + return $self->print->config->gcode_arcs + ? Slic3r::GCode::ArcFitting->new(config => $self->print->config) : undef; } @@ -45,44 +45,46 @@ sub process_layer { my ($layer, $object_copies) = @_; my $gcode = ""; + my $object = $layer->object; + # check whether we're going to apply spiralvase logic my $spiralvase = defined $self->spiralvase - && ($layer->id > 0 || $self->gcodegen->config->brim_width == 0) - && ($layer->id >= $self->gcodegen->config->skirt_height && $self->gcodegen->config->skirt_height != -1) - && ($layer->id >= $self->gcodegen->config->bottom_solid_layers); + && ($layer->id > 0 || $self->print->config->brim_width == 0) + && ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1) + && !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions}); # if we're going to apply spiralvase to this layer, disable loop clipping $self->gcodegen->enable_loop_clipping(!$spiralvase); if (!$self->second_layer_things_done && $layer->id == 1) { - for my $t (grep $self->extruders->[$_], 0 .. $#{$self->gcodegen->config->temperature}) { + for my $t (grep $self->extruders->[$_], 0 .. $#{$self->print->config->temperature}) { $gcode .= $self->gcodegen->set_temperature($self->extruders->[$t]->temperature, 0, $t) if $self->print->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature; } - $gcode .= $self->gcodegen->set_bed_temperature($self->gcodegen->config->bed_temperature) - if $self->gcodegen->config->bed_temperature && $self->gcodegen->config->bed_temperature != $self->gcodegen->config->first_layer_bed_temperature; + $gcode .= $self->gcodegen->set_bed_temperature($self->print->config->bed_temperature) + if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature; $self->second_layer_things_done(1); } # set new layer - this will change Z and force a retraction if retract_layer_change is enabled $gcode .= $self->gcodegen->change_layer($layer); - $gcode .= $self->gcodegen->replace_variables($self->gcodegen->config->layer_gcode, { + $gcode .= $self->gcodegen->replace_variables($self->print->config->layer_gcode, { layer_num => $self->gcodegen->layer->id, - }) . "\n" if $self->gcodegen->config->layer_gcode; + }) . "\n" if $self->print->config->layer_gcode; # extrude skirt - if (((values %{$self->skirt_done}) < $self->gcodegen->config->skirt_height || $self->gcodegen->config->skirt_height == -1) + if (((values %{$self->skirt_done}) < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) && !$self->skirt_done->{$layer->print_z}) { $self->gcodegen->set_shift(@{$self->shift}); $gcode .= $self->gcodegen->set_extruder($self->extruders->[0]); # skip skirt if we have a large brim - if ($layer->id < $self->gcodegen->config->skirt_height || $self->gcodegen->config->skirt_height == -1) { + if ($layer->id < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) { # distribute skirt loops across all extruders my @skirt_loops = @{$self->print->skirt}; for my $i (0 .. $#skirt_loops) { # when printing layers > 0 ignore 'min_skirt_length' and # just use the 'skirts' setting; also just use the current extruder - last if ($layer->id > 0) && ($i >= $self->gcodegen->config->skirts); + last if ($layer->id > 0) && ($i >= $self->print->config->skirts); $gcode .= $self->gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ]) if $layer->id == 0; $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt'); @@ -94,7 +96,7 @@ sub process_layer { # extrude brim if (!$self->brim_done) { - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]); + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->print->objects->[0]->config->support_material_extruder-1]); $self->gcodegen->set_shift(@{$self->shift}); $gcode .= $self->gcodegen->extrude_loop($_, 'brim') for @{$self->print->brim}; $self->brim_done(1); @@ -109,15 +111,17 @@ sub process_layer { # extrude support material before other things because it might use a lower Z # and also because we avoid travelling on other things when printing it - if ($self->print->has_support_material && $layer->isa('Slic3r::Layer::Support')) { + if ($layer->isa('Slic3r::Layer::Support')) { if ($layer->support_interface_fills->count > 0) { - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_interface_extruder-1]); - $gcode .= $self->gcodegen->extrude_path($_, 'support material interface') + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_interface_extruder-1]); + my %params = (speed => $object->config->support_material_speed*60); + $gcode .= $self->gcodegen->extrude_path($_, 'support material interface', %params) for @{$layer->support_interface_fills->chained_path_from($self->gcodegen->last_pos, 0)}; } if ($layer->support_fills->count > 0) { - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]); - $gcode .= $self->gcodegen->extrude_path($_, 'support material') + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_extruder-1]); + my %params = (speed => $object->config->support_material_speed*60); + $gcode .= $self->gcodegen->extrude_path($_, 'support material', %params) for @{$layer->support_fills->chained_path_from($self->gcodegen->last_pos, 0)}; } } @@ -126,16 +130,17 @@ sub process_layer { my @region_ids = 0 .. ($self->print->regions_count-1); if ($self->gcodegen->multiple_extruders) { my $last_extruder = $self->gcodegen->extruder; - my $best_region_id = first { $self->print->regions->[$_]->extruders->{perimeter} eq $last_extruder } @region_ids; + my $best_region_id = first { $self->print->regions->[$_]->config->perimeter_extruder-1 eq $last_extruder } @region_ids; @region_ids = ($best_region_id, grep $_ != $best_region_id, @region_ids) if $best_region_id; } foreach my $region_id (@region_ids) { my $layerm = $layer->regions->[$region_id] or next; my $region = $self->print->regions->[$region_id]; + $self->gcodegen->region($region); my @islands = (); - if ($self->gcodegen->config->avoid_crossing_perimeters) { + if ($self->print->config->avoid_crossing_perimeters) { push @islands, { perimeters => [], fills => [] } for 1 .. (@{$layer->slices} || 1); # make sure we have at least one island hash to avoid failure of the -1 subscript below PERIMETER: foreach my $perimeter (@{$layerm->perimeters}) { @@ -166,8 +171,8 @@ sub process_layer { foreach my $island (@islands) { # give priority to infill if we were already using its extruder and it wouldn't # be good for perimeters - if ($self->gcodegen->config->infill_first - || ($self->gcodegen->multiple_extruders && $region->extruders->{infill} eq $self->gcodegen->extruder) && $region->extruders->{infill} ne $region->extruders->{perimeter}) { + if ($self->print->config->infill_first + || ($self->gcodegen->multiple_extruders && $region->config->infill_extruder-1 == $self->gcodegen->extruder->id && $region->config->infill_extruder != $region->config->perimeter_extruder)) { $gcode .= $self->_extrude_infill($island, $region); $gcode .= $self->_extrude_perimeters($island, $region); } else { @@ -184,11 +189,11 @@ sub process_layer { # apply vibration limit if enabled $gcode = $self->vibration_limit->process($gcode) - if $self->gcodegen->config->vibration_limit != 0; + if $self->print->config->vibration_limit != 0; # apply arc fitting if enabled $gcode = $self->arc_fitting->process($gcode) - if $self->gcodegen->config->gcode_arcs; + if $self->print->config->gcode_arcs; return $gcode; } @@ -200,7 +205,7 @@ sub _extrude_perimeters { return "" if !@{ $island->{perimeters} }; my $gcode = ""; - $gcode .= $self->gcodegen->set_extruder($region->extruders->{perimeter}); + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->perimeter_extruder-1]); $gcode .= $self->gcodegen->extrude($_, 'perimeter') for @{ $island->{perimeters} }; return $gcode; } @@ -212,7 +217,7 @@ sub _extrude_infill { return "" if !@{ $island->{fills} }; my $gcode = ""; - $gcode .= $self->gcodegen->set_extruder($region->extruders->{infill}); + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->infill_extruder-1]); for my $fill (@{ $island->{fills} }) { if ($fill->isa('Slic3r::ExtrusionPath::Collection')) { $gcode .= $self->gcodegen->extrude($_, 'fill') diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index d490597d..d44a06b6 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -12,9 +12,9 @@ use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex inter offset2 union union_pt_chained JT_ROUND JT_SQUARE); use Slic3r::Print::State ':steps'; -has 'config' => (is => 'rw', default => sub { Slic3r::Config::Print->new }, trigger => \&init_config); -has 'default_object_config' => (is => 'rw', default => sub { Slic3r::Config::PrintObject->new }); -has 'default_region_config' => (is => 'rw', default => sub { Slic3r::Config::PrintRegion->new }); +has 'config' => (is => 'ro', default => sub { Slic3r::Config::Print->new }); +has 'default_object_config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new }); +has 'default_region_config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new }); has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'objects' => (is => 'rw', default => sub {[]}); has 'status_cb' => (is => 'rw'); @@ -28,52 +28,12 @@ has 'skirt' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection-> # ordered collection of extrusion paths to build a brim has 'brim' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); -sub BUILD { - my $self = shift; - - # call this manually because the 'default' coderef doesn't trigger the trigger - $self->init_config; -} - -# this method needs to be idempotent -sub init_config { - my $self = shift; - - # legacy with existing config files - $self->config->set('first_layer_height', $self->config->layer_height) - if !$self->config->first_layer_height; - $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); - $self->config->set_ifndef('top_solid_layers', $self->config->solid_layers); - $self->config->set_ifndef('bottom_solid_layers', $self->config->solid_layers); - - # 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'; - - # enforce some settings when spiral_vase is set - if ($self->config->spiral_vase) { - $self->config->set('perimeters', 1); - $self->config->set('fill_density', 0); - $self->config->set('top_solid_layers', 0); - $self->config->set('support_material', 0); - $self->config->set('support_material_enforce_layers', 0); - $self->config->set('retract_layer_change', [0]); # TODO: only apply this to the spiral layers - } - - # force all retraction lift values to be the same - $self->config->set('retract_lift', [ map $self->config->retract_lift->[0], @{$self->config->retract_lift} ]); -} - sub apply_config { my ($self, $config) = @_; $self->config->apply_dynamic($config); $self->default_object_config->apply_dynamic($config); $self->default_region_config->apply_dynamic($config); - $self->init_config; } sub has_support_material { @@ -261,11 +221,21 @@ sub init_extruders { } for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) { + # make sure print config contains a value for all extruders + my %extruder_config = (); + foreach my $opt_key (@{&Slic3r::Extruder::OPTIONS}) { + my $value = $self->config->get($opt_key); + if (!defined $value->[$extruder_id]) { + $value->[$extruder_id] = $value->[0]; + $self->config->set($opt_key, $value); + } + $extruder_config{$opt_key} = $value->[$extruder_id]; + } + $self->extruders->[$extruder_id] = Slic3r::Extruder->new( - config => $self->config, id => $extruder_id, - map { $_ => $self->config->get($_)->[$extruder_id] // $self->config->get($_)->[0] } #/ - @{&Slic3r::Extruder::OPTIONS} + use_relative_e_distances => $self->config->use_relative_e_distances, + %extruder_config, ); } @@ -614,16 +584,17 @@ sub make_skirt { # skirt may be printed on several layers, having distinct layer heights, # but loops must be aligned so can't vary width/spacing # TODO: use each extruder's own flow + my $region0_config = $self->regions->[0]->config; + my $first_layer_height = $self->objects->[0]->config->get_value('first_layer_height'); my $flow = Slic3r::Flow->new( - width => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width), + width => ($region0_config->first_layer_extrusion_width || $region0_config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, nozzle_diameter => $self->config->nozzle_diameter->[0], - layer_height => $self->config->get_abs_value('first_layer_height'), + layer_height => $first_layer_height, bridge_flow_ratio => 0, ); my $spacing = $flow->spacing; - my $first_layer_height = $self->config->get_value('first_layer_height'); my @extruders_e_per_mm = (); my $extruder_idx = 0; @@ -664,10 +635,10 @@ sub make_brim { # brim is only printed on first layer and uses support material extruder my $flow = Slic3r::Flow->new( - width => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width), + width => ($self->regions->[0]->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, - nozzle_diameter => $self->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ], - layer_height => $self->config->get_abs_value('first_layer_height'), + nozzle_diameter => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ], + layer_height => $self->objects->[0]->config->get_abs_value('first_layer_height'), bridge_flow_ratio => 0, ); @@ -737,34 +708,28 @@ sub write_gcode { print $fh "; $_\n" foreach split /\R/, $self->config->notes; print $fh "\n" if $self->config->notes; - for (qw(layer_height perimeters top_solid_layers bottom_solid_layers fill_density perimeter_speed infill_speed travel_speed)) { - printf $fh "; %s = %s\n", $_, $self->config->$_; - } - for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) { - printf $fh "; %s = %s\n", $_, $self->config->$_->[0]; - } - + my $layer_height = $self->objects->[0]->config->layer_height; for my $region_id (0..$#{$self->regions}) { printf $fh "; perimeters extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER)->width; + $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height)->width; printf $fh "; infill extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL)->width; + $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL, $layer_height)->width; printf $fh "; solid infill extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL)->width; + $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL, $layer_height)->width; printf $fh "; top infill extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width; + $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height)->width; printf $fh "; support material extrusion width = %.2fmm\n", $self->objects->[0]->support_material_flow->width if $self->has_support_material; printf $fh "; first layer extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, 0, 1)->width - if ($self->regions->[$region_id]->config->first_layer_extrusion_width != 0); + $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1)->width + if ($self->regions->[$region_id]->config->first_layer_extrusion_width ne '0'); print $fh "\n"; } # set up our extruder object my $gcodegen = Slic3r::GCode->new( - config => $self->config, + print_config => $self->config, extra_variables => $self->extra_variables, extruders => $self->extruders, # we should only pass the *used* extruders (but maintain the Tx indices right!) layer_count => $self->layer_count, @@ -941,13 +906,11 @@ sub write_gcode { $extruder->absolute_E, $extruder->extruded_volume/1000; } - if ($self->config->gcode_comments) { - # append full config - print $fh "\n"; - foreach my $opt_key (sort @{$self->config->get_keys}) { - next if $Slic3r::Config::Options->{$opt_key}{shortcut}; - printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key); - } + # append full config + print $fh "\n"; + foreach my $opt_key (sort @{$self->config->get_keys}) { + next if $Slic3r::Config::Options->{$opt_key}{shortcut}; + printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key); } # close our gcode file diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 600a0dcc..bf4c2efb 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -521,13 +521,16 @@ sub clip_fill_surfaces { sub bridge_over_infill { my $self = shift; - return if $self->config->fill_density == 1; - for my $layer_id (1..$#{$self->layers}) { - my $layer = $self->layers->[$layer_id]; - my $lower_layer = $self->layers->[$layer_id-1]; + for my $region_id (0..$#{$self->print->regions}) { + my $fill_density = $self->print->regions->[$region_id]->config->fill_density; + next if $fill_density == 1 || $fill_density == 0; - foreach my $layerm (@{$layer->regions}) { + for my $layer_id (1..$#{$self->layers}) { + my $layer = $self->layers->[$layer_id]; + my $layerm = $layer->regions->[$region_id]; + my $lower_layer = $self->layers->[$layer_id-1]; + # compute the areas needing bridge math my @internal_solid = @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNALSOLID)}; my @lower_internal = map @{$_->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)}, @{$lower_layer->regions}; @@ -861,7 +864,8 @@ sub generate_support_material { && $self->layer_count >= 2; my $s = Slic3r::Print::SupportMaterial->new( - config => $self->config, + print_config => $self->print->config, + object_config => $self->config, flow => $self->support_material_flow, interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), ); diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 4cb929e0..ff05d5bc 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -53,7 +53,7 @@ sub flow { role => $role, nozzle_diameter => $nozzle_diameter, layer_height => $layer_height, - bridge_flow_ratio => ($bridge ? $self->config->bridge_flow_ratio : 0), + bridge_flow_ratio => ($bridge ? $self->print->config->bridge_flow_ratio : 0), ); } diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 291268bb..38949d22 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -3,13 +3,15 @@ use Moo; use List::Util qw(sum min max); use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(scale scaled_epsilon PI rad2deg deg2rad); use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2 intersection_pl); use Slic3r::Surface ':types'; -has 'config' => (is => 'rw', required => 1); -has 'flow' => (is => 'rw', required => 1); +has 'print_config' => (is => 'rw', required => 1); +has 'object_config' => (is => 'rw', required => 1); +has 'flow' => (is => 'rw', required => 1); use constant DEBUG_CONTACT_ONLY => 0; @@ -73,8 +75,8 @@ sub contact_area { # if user specified a custom angle threshold, convert it to radians my $threshold_rad; - if ($self->config->support_material_threshold) { - $threshold_rad = deg2rad($self->config->support_material_threshold + 1); # +1 makes the threshold inclusive + if ($self->object_config->support_material_threshold) { + $threshold_rad = deg2rad($self->object_config->support_material_threshold + 1); # +1 makes the threshold inclusive Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); } @@ -86,9 +88,9 @@ sub contact_area { # so $layer_id == 0 means first object layer # and $layer->id == 0 means first print layer (including raft) - if ($self->config->raft_layers == 0) { + if ($self->object_config->raft_layers == 0) { next if $layer_id == 0; - } elsif (!$self->config->support_material) { + } elsif (!$self->object_config->support_material) { # if we are only going to generate raft just check # the 'overhangs' of the first object layer last if $layer_id > 0; @@ -110,8 +112,8 @@ sub contact_area { # If a threshold angle was specified, use a different logic for detecting overhangs. if (defined $threshold_rad - || $layer_id < $self->config->support_material_enforce_layers - || $self->config->raft_layers > 0) { + || $layer_id < $self->object_config->support_material_enforce_layers + || $self->object_config->raft_layers > 0) { my $d = defined $threshold_rad ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad)) : 0; @@ -170,8 +172,8 @@ sub contact_area { # now apply the contact areas to the layer were they need to be made { # get the average nozzle diameter used on this layer - my @nozzle_diameters = map $_->nozzle_diameter, - map { $_->perimeter_flow, $_->solid_infill_flow } + my @nozzle_diameters = map $self->print_config->nozzle_diameter->[$_], + map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1 } @{$layer->regions}; my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters; @@ -179,7 +181,7 @@ sub contact_area { ###$contact_z = $layer->print_z - $layer->height; # ignore this contact area if it's too low - next if $contact_z < $self->config->get_value('first_layer_height'); + next if $contact_z < $self->object_config->get_value('first_layer_height'); $contact{$contact_z} = [ @contact ]; $overhang{$contact_z} = [ @overhang ]; @@ -242,25 +244,25 @@ sub support_layers_z { # determine layer height for any non-contact layer # we use max() to prevent many ultra-thin layers to be inserted in case # layer_height > nozzle_diameter * 0.75 - my $nozzle_diameter = $self->flow->nozzle_diameter; + my $nozzle_diameter = $self->print_config->nozzle_diameter->[$self->object_config->support_material_extruder-1]; my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75); my @z = sort { $a <=> $b } @$contact_z, @$top_z, (map $_ + $nozzle_diameter, @$top_z); # enforce first layer height - my $first_layer_height = $self->config->get_value('first_layer_height'); + my $first_layer_height = $self->object_config->get_value('first_layer_height'); shift @z while @z && $z[0] <= $first_layer_height; unshift @z, $first_layer_height; # add raft layers by dividing the space between first layer and # first contact layer evenly - if ($self->config->raft_layers > 1 && @z >= 2) { + if ($self->object_config->raft_layers > 1 && @z >= 2) { # $z[1] is last raft layer (contact layer for the first layer object) - my $height = ($z[1] - $z[0]) / ($self->config->raft_layers - 1); + my $height = ($z[1] - $z[0]) / ($self->object_config->raft_layers - 1); splice @z, 1, 0, map { int($_*100)/100 } map { $z[0] + $height * $_ } - 0..($self->config->raft_layers - 1); + 0..($self->object_config->raft_layers - 1); } for (my $i = $#z; $i >= 0; $i--) { @@ -291,7 +293,7 @@ sub generate_interface_layers { # let's now generate interface layers below contact areas my %interface = (); # layer_id => [ polygons ] - my $interface_layers = $self->config->support_material_interface_layers; + my $interface_layers = $self->object_config->support_material_interface_layers; for my $layer_id (0 .. $#$support_z) { my $z = $support_z->[$layer_id]; my $this = $contact->{$z} // next; @@ -339,7 +341,7 @@ sub generate_base_layers { # in case we have no interface layers, look at upper contact # (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty) my @upper_contact = (); - if ($self->config->support_material_interface_layers <= 1) { + if ($self->object_config->support_material_interface_layers <= 1) { @upper_contact = @{ $contact->{$support_z->[$i+1]} || [] }; } @@ -395,8 +397,8 @@ sub generate_toolpaths { Slic3r::debugf "Generating patterns\n"; # prepare fillers - my $pattern = $self->config->support_material_pattern; - my @angles = ($self->config->support_material_angle); + my $pattern = $self->object_config->support_material_pattern; + my @angles = ($self->object_config->support_material_angle); if ($pattern eq 'rectilinear-grid') { $pattern = 'rectilinear'; push @angles, $angles[0] + 90; @@ -407,10 +409,10 @@ sub generate_toolpaths { support => $object->fill_maker->filler($pattern), ); - my $interface_angle = $self->config->support_material_angle + 90; - my $interface_spacing = $self->config->support_material_interface_spacing + $flow->spacing; + my $interface_angle = $self->object_config->support_material_angle + 90; + my $interface_spacing = $self->object_config->support_material_interface_spacing + $flow->spacing; my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing; - my $support_spacing = $self->config->support_material_spacing + $flow->spacing; + my $support_spacing = $self->object_config->support_material_spacing + $flow->spacing; my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing; my $process_layer = sub { @@ -441,7 +443,7 @@ sub generate_toolpaths { # contact my $contact_infill = []; - if ($self->config->support_material_interface_layers == 0) { + if ($self->object_config->support_material_interface_layers == 0) { # if no interface layers were requested we treat the contact layer # exactly as a generic base layer push @$base, @$contact; @@ -561,7 +563,7 @@ sub generate_toolpaths { # base flange if ($layer_id == 0) { $filler = $fillers{interface}; - $filler->angle($self->config->support_material_angle + 90); + $filler->angle($self->object_config->support_material_angle + 90); $density = 0.5; $flow_spacing = $object->print->first_layer_support_material_flow->spacing; } else { @@ -609,7 +611,7 @@ sub generate_toolpaths { }; Slic3r::parallelize( - threads => $self->config->threads, + threads => $self->print_config->threads, items => [ 0 .. $#{$object->support_layers} ], thread_cb => sub { my $q = shift; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 3a394431..2e88268a 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -99,20 +99,21 @@ sub model { sub init_print { my ($model_name, %params) = @_; - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config->new; $config->apply($params{config}) if $params{config}; $config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE}; - my $print = Slic3r::Print->new(config => $config); + my $print = Slic3r::Print->new; + $print->apply_config($config); $model_name = [$model_name] if ref($model_name) ne 'ARRAY'; for my $model (map model($_, %params), @$model_name) { die "Unknown model in test" if !defined $model; if (defined $params{duplicate} && $params{duplicate} > 1) { - $model->duplicate($params{duplicate} // 1, $config->min_object_distance); + $model->duplicate($params{duplicate} // 1, $print->config->min_object_distance); } - $model->arrange_objects($config->min_object_distance); - $model->center_instances_around_point($config->print_center); + $model->arrange_objects($print->config->min_object_distance); + $model->center_instances_around_point($print->config->print_center); $print->add_model_object($_) for @{$model->objects}; } $print->validate; diff --git a/slic3r.pl b/slic3r.pl index 0be1219d..beea8037 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -153,12 +153,12 @@ if (@ARGV) { # slicing from command line } my $print = Slic3r::Print->new( - config => $config, status_cb => sub { my ($percent, $message) = @_; printf "=> %s\n", $message; }, ); + $print->apply_config($config); foreach my $model_object (@{$model->objects}) { $print->auto_assign_extruders($model_object); $print->add_model_object($model_object); diff --git a/t/cooling.t b/t/cooling.t index 87d3dff3..6fc5cf71 100644 --- a/t/cooling.t +++ b/t/cooling.t @@ -13,10 +13,14 @@ use Slic3r; use Slic3r::Test; sub buffer { - my $config = shift || Slic3r::Config->new_from_defaults; + my $config = shift || Slic3r::Config->new; + + my $print_config = Slic3r::Config::Print->new; + $print_config->apply_dynamic($config); + my $buffer = Slic3r::GCode::CoolingBuffer->new( - config => $config, - gcodegen => Slic3r::GCode->new(config => $config, layer_count => 10, extruders => []), + config => $print_config, + gcodegen => Slic3r::GCode->new(print_config => $print_config, layer_count => 10, extruders => []), ); return $buffer; } diff --git a/t/gcode.t b/t/gcode.t index c8db2f81..bd5677fc 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -14,9 +14,8 @@ use Slic3r::Test; { my $gcodegen = Slic3r::GCode->new( - config => Slic3r::Config->new_from_defaults, - layer_count => 1, - extruders => [], + layer_count => 1, + extruders => [], ); $gcodegen->set_shift(10, 10); is_deeply $gcodegen->last_pos->arrayref, [scale -10, scale -10], 'last_pos is shifted correctly'; diff --git a/t/shells.t b/t/shells.t index 10da9055..541c7cb7 100644 --- a/t/shells.t +++ b/t/shells.t @@ -140,6 +140,9 @@ use Slic3r::Test; { my $config = Slic3r::Config->new_from_defaults; + $config->set('perimeters', 1); + $config->set('fill_density', 0); + $config->set('top_solid_layers', 0); $config->set('spiral_vase', 1); $config->set('bottom_solid_layers', 0); $config->set('skirts', 0); diff --git a/t/support.t b/t/support.t index b7d1810d..bd82d0ce 100644 --- a/t/support.t +++ b/t/support.t @@ -21,16 +21,16 @@ use Slic3r::Test; my $test = sub { my $print = Slic3r::Test::init_print('20mm_cube', config => $config); $print->init_extruders; - my $flow = $print->support_material_flow; + my $flow = $print->objects->[0]->support_material_flow; my $support_z = Slic3r::Print::SupportMaterial - ->new(config => $config, flow => $flow) + ->new(object_config => $print->objects->[0]->config, print_config => $print->config, flow => $flow) ->support_layers_z(\@contact_z, \@top_z, $config->layer_height); is $support_z->[0], $config->first_layer_height, 'first layer height is honored'; is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0, 'no null or negative support layers'; - is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $flow->nozzle_diameter + epsilon } 1..$#$support_z), 0, + is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $config->nozzle_diameter->[0] + epsilon } 1..$#$support_z), 0, 'no layers thicker than nozzle diameter'; my $wrong_top_spacing = 0; @@ -40,7 +40,7 @@ use Slic3r::Test; # check that first support layer above this top surface is spaced with nozzle diameter $wrong_top_spacing = 1 - if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $flow->nozzle_diameter; + if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $config->nozzle_diameter->[0]; } ok !$wrong_top_spacing, 'layers above top surfaces are spaced correctly'; }; diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 4643c8b7..cecd00a3 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -899,6 +899,7 @@ class PrintObjectConfig : public virtual StaticConfig public: ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent first_layer_height; + ConfigOptionBool infill_only_where_needed; ConfigOptionFloat layer_height; ConfigOptionInt raft_layers; ConfigOptionBool support_material; @@ -921,6 +922,7 @@ class PrintObjectConfig : public virtual StaticConfig this->extrusion_width.percent = false; this->first_layer_height.value = 0.35; this->first_layer_height.percent = false; + this->infill_only_where_needed.value = false; this->layer_height.value = 0.4; this->raft_layers.value = 0; this->support_material.value = false; @@ -941,6 +943,7 @@ class PrintObjectConfig : public virtual StaticConfig ConfigOption* option(const t_config_option_key opt_key, bool create = false) { if (opt_key == "extrusion_width") return &this->extrusion_width; if (opt_key == "first_layer_height") return &this->first_layer_height; + if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed; if (opt_key == "layer_height") return &this->layer_height; if (opt_key == "raft_layers") return &this->raft_layers; if (opt_key == "support_material") return &this->support_material; @@ -972,7 +975,6 @@ class PrintRegionConfig : public virtual StaticConfig ConfigOptionInt infill_extruder; ConfigOptionFloatOrPercent infill_extrusion_width; ConfigOptionInt infill_every_layers; - ConfigOptionBool infill_only_where_needed; ConfigOptionInt perimeter_extruder; ConfigOptionFloatOrPercent perimeter_extrusion_width; ConfigOptionInt perimeters; @@ -999,7 +1001,6 @@ class PrintRegionConfig : public virtual StaticConfig this->infill_extrusion_width.value = 0; this->infill_extrusion_width.percent = false; this->infill_every_layers.value = 1; - this->infill_only_where_needed.value = false; this->perimeter_extruder.value = 1; this->perimeter_extrusion_width.value = 0; this->perimeter_extrusion_width.percent = false; @@ -1025,7 +1026,6 @@ class PrintRegionConfig : public virtual StaticConfig if (opt_key == "infill_extruder") return &this->infill_extruder; if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width; if (opt_key == "infill_every_layers") return &this->infill_every_layers; - if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed; if (opt_key == "perimeter_extruder") return &this->perimeter_extruder; if (opt_key == "perimeter_extrusion_width") return &this->perimeter_extrusion_width; if (opt_key == "perimeters") return &this->perimeters; @@ -1332,6 +1332,15 @@ class PrintConfig : public virtual StaticConfig return NULL; }; + + std::string get_extrusion_axis() { + if (this->gcode_flavor == gcfMach3) { + return std::string("A"); + } else if (this->gcode_flavor == gcfNoExtrusion) { + return std::string(""); + } + return this->extrusion_axis; + } }; class DynamicPrintConfig : public DynamicConfig diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 5c3966e1..b7f6f3ec 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -41,6 +41,7 @@ %code{% THIS->apply(*other, true); %}; std::vector get_keys() %code{% THIS->keys(&RETVAL); %}; + std::string get_extrusion_axis(); }; %name{Slic3r::Config::PrintRegion} class PrintRegionConfig {