diff --git a/MANIFEST b/MANIFEST index 1568fc49..96135c3e 100644 --- a/MANIFEST +++ b/MANIFEST @@ -23,6 +23,7 @@ lib/Slic3r/Format/AMF.pm lib/Slic3r/Format/AMF/Parser.pm lib/Slic3r/Format/OBJ.pm lib/Slic3r/Format/STL.pm +lib/Slic3r/GCode.pm lib/Slic3r/Geometry.pm lib/Slic3r/Geometry/Clipper.pm lib/Slic3r/GUI.pm diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index e9159e0f..5bf8bb30 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -37,6 +37,7 @@ use Slic3r::Flow; use Slic3r::Format::AMF; use Slic3r::Format::OBJ; use Slic3r::Format::STL; +use Slic3r::GCode; use Slic3r::Geometry qw(PI); use Slic3r::Layer; use Slic3r::Line; @@ -59,7 +60,6 @@ our $output_filename_format = '[input_filename_base].gcode'; our $post_process = []; # printer options -our $nozzle_diameter = 0.5; our $print_center = [100,100]; # object will be centered around this point our $z_offset = 0; our $gcode_flavor = 'reprap'; @@ -70,13 +70,17 @@ 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; our $bed_temperature = 0; our $first_layer_bed_temperature; +# extruders +our $extruders = []; +our $nozzle_diameter = [0.5]; +our $filament_diameter = [3]; # mm +our $extrusion_multiplier = [1]; + # speed options our $travel_speed = 130; # mm/s our $perimeter_speed = 30; # mm/s diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index d7ea2369..6e893844 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -35,12 +35,6 @@ our $Options = { }, # printer options - 'nozzle_diameter' => { - label => 'Nozzle diameter (mm)', - cli => 'nozzle-diameter=f', - type => 'f', - important => 1, - }, 'print_center' => { label => 'Print center (mm)', cli => 'print-center=s', @@ -86,19 +80,33 @@ our $Options = { type => 'bool', }, - # filament options - 'filament_diameter' => { - label => 'Diameter (mm)', - cli => 'filament-diameter=f', + # extruders options + 'nozzle_diameter' => { + label => 'Nozzle diameter (mm)', + cli => 'nozzle-diameter=f@', type => 'f', important => 1, + serialize => sub { join ',', @{$_[0]} }, + deserialize => sub { [ split /,/, $_[0] ] }, + }, + 'filament_diameter' => { + label => 'Diameter (mm)', + cli => 'filament-diameter=f@', + type => 'f', + important => 1, + serialize => sub { join ',', @{$_[0]} }, + deserialize => sub { [ split /,/, $_[0] ] }, }, 'extrusion_multiplier' => { label => 'Extrusion multiplier', - cli => 'extrusion-multiplier=f', + cli => 'extrusion-multiplier=f@', type => 'f', aliases => [qw(filament_packing_density)], + serialize => sub { join ',', @{$_[0]} }, + deserialize => sub { [ split /,/, $_[0] ] }, }, + + # filament options 'first_layer_temperature' => { label => 'First layer temperature (°C)', cli => 'first-layer-temperature=i', @@ -661,28 +669,34 @@ sub validate { # --filament-diameter die "Invalid value for --filament-diameter\n" - if $Slic3r::filament_diameter < 1; + if grep $_ < 1, @$Slic3r::filament_diameterì; # --nozzle-diameter die "Invalid value for --nozzle-diameter\n" - if $Slic3r::nozzle_diameter < 0; + if grep $_ < 0, @$Slic3r::nozzle_diameter; die "--layer-height can't be greater than --nozzle-diameter\n" - if $Slic3r::layer_height > $Slic3r::nozzle_diameter; + if grep $Slic3r::layer_height > $_, @$Slic3r::nozzle_diameter; die "First layer height can't be greater than --nozzle-diameter\n" - if $Slic3r::_first_layer_height > $Slic3r::nozzle_diameter; + if grep $Slic3r::_first_layer_height > $_, @$Slic3r::nozzle_diameter; + + # initialize extruder(s) + $Slic3r::extruders = []; + push @$Slic3r::extruders, Slic3r::Extruder->new( + nozzle_diameter => $Slic3r::nozzle_diameter->[0], + filament_diameter => $Slic3r::filament_diameter->[0], + extrusion_multiplier => $Slic3r::extrusion_multiplier->[0], + ); # calculate flow - $Slic3r::flow = Slic3r::Flow->new; - $Slic3r::flow->calculate($Slic3r::extrusion_width); + $Slic3r::flow = $Slic3r::extruders->[0]->make_flow(width => $Slic3r::extrusion_width); if ($Slic3r::first_layer_extrusion_width) { - $Slic3r::first_layer_flow = Slic3r::Flow->new(layer_height => $Slic3r::_first_layer_height); - $Slic3r::first_layer_flow->calculate($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::Flow->new; - $Slic3r::perimeters_flow->calculate($Slic3r::perimeters_extrusion_width || $Slic3r::extrusion_width); - - $Slic3r::infill_flow = Slic3r::Flow->new; - $Slic3r::infill_flow->calculate($Slic3r::infill_extrusion_width || $Slic3r::extrusion_width); + $Slic3r::perimeters_flow = $Slic3r::extruders->[0]->make_flow(width => $Slic3r::perimeters_extrusion_width || $Slic3r::extrusion_width); + $Slic3r::infill_flow = $Slic3r::extruders->[0]->make_flow(width => $Slic3r::infill_extrusion_width || $Slic3r::extrusion_width); Slic3r::debugf "Default flow width = %s, spacing = %s, min_spacing = %s\n", $Slic3r::flow->width, $Slic3r::flow->spacing, $Slic3r::flow->min_spacing; @@ -717,8 +731,9 @@ sub validate { # --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 $Slic3r::infill_every_layers * $Slic3r::layer_height > $Slic3r::nozzle_diameter; + if grep $Slic3r::infill_every_layers * $Slic3r::layer_height > $_, @$Slic3r::nozzle_diameter; # --scale die "Invalid value for --scale\n" diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 6a666519..1dd21c31 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -1,422 +1,26 @@ package Slic3r::Extruder; use Moo; -use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale); +use Slic3r::Geometry qw(PI); -has 'layer' => (is => 'rw'); -has 'shift_x' => (is => 'rw', default => sub {0} ); -has 'shift_y' => (is => 'rw', default => sub {0} ); -has 'z' => (is => 'rw', default => sub {0} ); -has 'speed' => (is => 'rw'); +has 'nozzle_diameter' => (is => 'rw', required => 1); +has 'filament_diameter' => (is => 'rw', required => 1); +has 'extrusion_multiplier' => (is => 'rw', required => 1); -has 'extrusion_distance' => (is => 'rw', default => sub {0} ); -has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds -has 'total_extrusion_length' => (is => 'rw', default => sub {0} ); -has 'retracted' => (is => 'rw', default => sub {1} ); # this spits out some plastic at start -has 'lifted' => (is => 'rw', default => sub {0} ); -has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } ); -has 'last_speed' => (is => 'rw', default => sub {""}); -has 'last_fan_speed' => (is => 'rw', default => sub {0}); -has 'dec' => (is => 'ro', default => sub { 3 } ); +has 'e_per_mmc' => (is => 'rw'); -# calculate speeds (mm/min) -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'), - }}, -); - -my %role_speeds = ( - &EXTR_ROLE_PERIMETER => 'perimeter', - &EXTR_ROLE_SMALLPERIMETER => 'small_perimeter', - &EXTR_ROLE_EXTERNAL_PERIMETER => 'external_perimeter', - &EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER => 'perimeter', - &EXTR_ROLE_FILL => 'infill', - &EXTR_ROLE_SOLIDFILL => 'solid_infill', - &EXTR_ROLE_TOPSOLIDFILL => 'top_solid_infill', - &EXTR_ROLE_BRIDGE => 'bridge', - &EXTR_ROLE_SKIRT => 'perimeter', - &EXTR_ROLE_SUPPORTMATERIAL => 'perimeter', -); - -use Slic3r::Geometry qw(points_coincide PI X Y); - -sub change_layer { +sub BUILD { my $self = shift; - my ($layer) = @_; - - $self->layer($layer); - my $z = $Slic3r::z_offset + $layer->print_z * $Slic3r::scaling_factor; - - my $gcode = ""; - - $gcode .= $self->retract(move_z => $z); - $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; - - return $gcode; + $self->e_per_mmc( + $Slic3r::scaling_factor + * $self->extrusion_multiplier + * (4 / (($self->filament_diameter ** 2) * PI)) + ); } -sub extrude { +sub make_flow { my $self = shift; - - if ($_[0]->isa('Slic3r::ExtrusionLoop')) { - $self->extrude_loop(@_); - } else { - $_[0]->deserialize; - $self->extrude_path(@_); - } -} - -sub extrude_loop { - my $self = shift; - my ($loop, $description) = @_; - - # extrude all loops ccw - $loop->deserialize; - $loop->polygon->make_counter_clockwise; - - # 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) { - 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); - } - my $start_at = $loop->nearest_point_to($last_pos); - - # split the loop at the starting point and make a path - my $extrusion_path = $loop->split_at($start_at); - $extrusion_path->deserialize; - - # clip the path to avoid the extruder to get exactly on the first point of the loop; - # if polyline was shorter than the clipping distance we'd get a null polyline, so - # we discard it in that case - $extrusion_path->clip_end(scale($self->layer ? $self->layer->flow->width : $Slic3r::flow->width) * 0.15); - return '' if !@{$extrusion_path->polyline}; - - # extrude along the path - return $self->extrude_path($extrusion_path, $description); -} - -sub extrude_path { - my $self = shift; - my ($path, $description, $recursive) = @_; - - $path->merge_continuous_lines; - - # detect arcs - if ($Slic3r::gcode_arcs && !$recursive) { - my $gcode = ""; - foreach my $arc_path ($path->detect_arcs) { - $arc_path->deserialize; - $gcode .= $self->extrude_path($arc_path, $description, 1); - } - return $gcode; - } - - my $gcode = ""; - - # 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/; - - if ($distance_from_last_pos >= $distance_threshold) { - $gcode .= $self->retract(travel_to => $path->points->[0]); - } - } - - # go to first point of extrusion path - $gcode .= $self->G0($path->points->[0], undef, 0, "move to first $description point") - if !points_coincide($self->last_pos, $path->points->[0]); - - # compensate retraction - $gcode .= $self->unretract if $self->retracted; - - # calculate extrusion length per distance unit - my $s = $path->flow_spacing || $self->layer ? $self->layer->flow->spacing : $Slic3r::flow->spacing; - my $h = $path->depth_layers * $self->layer->height; - my $w = ($s - ($self->layer ? $self->layer->flow->min_spacing : $Slic3r::flow->min_spacing) * $Slic3r::overlap_factor) / (1 - $Slic3r::overlap_factor); - - my $area; - if ($path->role == EXTR_ROLE_BRIDGE) { - $area = ($s**2) * PI/4; - } elsif ($w >= ($Slic3r::nozzle_diameter + $h)) { - # rectangle with semicircles at the ends - $area = $w * $h + ($h**2) / 4 * (PI - 4); - } else { - # rectangle with shrunk semicircles at the ends - $area = $Slic3r::nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4; - } - - my $e = $Slic3r::scaling_factor - * $area - * $Slic3r::extrusion_multiplier - * (4 / (($Slic3r::filament_diameter ** 2) * PI)); - - # extrude arc or line - $self->speed( $role_speeds{$path->role} || die "Unknown role: " . $path->role ); - my $path_length = 0; - if ($path->isa('Slic3r::ExtrusionPath::Arc')) { - $path_length = $path->length; - $gcode .= $self->G2_G3($path->points->[-1], $path->orientation, - $path->center, $e * $path_length, $description); - } else { - foreach my $line ($path->lines) { - my $line_length = $line->length; - $path_length += $line_length; - $gcode .= $self->G1($line->b, undef, $e * $line_length, $description); - } - } - - if ($Slic3r::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 / ($1/100) - : unscale($path_length) / $Slic3r::first_layer_speed * 60; - } - $self->elapsed_time($self->elapsed_time + $path_time); - } - - return $gcode; -} - -sub retract { - my $self = shift; - my %params = @_; - - return "" unless $Slic3r::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}) - ? undef - : [undef, $self->z + $Slic3r::retract_lift, 0, 'lift plate during retraction']; - - my $gcode = ""; - if (($Slic3r::g0 || $Slic3r::gcode_flavor eq 'mach3') && $params{travel_to}) { - if ($lift) { - # combine lift and retract - $lift->[2] = $retract->[2]; - $gcode .= $self->G0(@$lift); - } else { - # combine travel and 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}) { - # 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']; - $gcode .= $self->G0(@$travel); - $self->lifted(1); - } elsif ($lift) { - $gcode .= $self->G1(@$lift); - } - } - $self->retracted(1); - $self->lifted(1) if $lift; - - # reset extrusion distance during retracts - # this makes sure we leave sufficient precision in the firmware - if (!$Slic3r::use_relative_e_distances && $Slic3r::gcode_flavor !~ /^(?:mach3|makerbot)$/) { - $gcode .= "G92 " . $Slic3r::extrusion_axis . "0\n" if $Slic3r::extrusion_axis; - $self->extrusion_distance(0); - } - - return $gcode; -} - -sub unretract { - my $self = shift; - $self->retracted(0); - my $gcode = ""; - - if ($self->lifted) { - $gcode .= $self->G0(undef, $self->z - $Slic3r::retract_lift, 0, 'restore layer Z'); - $self->lifted(0); - } - - $self->speed('retract'); - $gcode .= $self->G0(undef, undef, ($Slic3r::retract_length + $Slic3r::retract_restart_extra), - "compensate retraction"); - - return $gcode; -} - -sub set_acceleration { - my $self = shift; - my ($acceleration) = @_; - return "" unless $Slic3r::acceleration; - - return sprintf "M201 E%s%s\n", - $acceleration, ($Slic3r::gcode_comments ? ' ; adjust acceleration' : ''); -} - -sub G0 { - my $self = shift; - return $self->G1(@_) if !($Slic3r::g0 || $Slic3r::gcode_flavor eq 'mach3'); - return $self->_G0_G1("G0", @_); -} - -sub G1 { - my $self = shift; - return $self->_G0_G1("G1", @_); -} - -sub _G0_G1 { - my $self = shift; - my ($gcode, $point, $z, $e, $comment) = @_; - my $dec = $self->dec; - - 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; #** - $self->last_pos($point); - } - if (defined $z && $z != $self->z) { - $self->z($z); - $gcode .= sprintf " Z%.${dec}f", $z; - } - - return $self->_Gx($gcode, $e, $comment); -} - -sub G2_G3 { - my $self = shift; - my ($point, $orientation, $center, $e, $comment) = @_; - my $dec = $self->dec; - - 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; #** - - # 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; - - $self->last_pos($point); - return $self->_Gx($gcode, $e, $comment); -} - -sub _Gx { - my $self = shift; - my ($gcode, $e, $comment) = @_; - my $dec = $self->dec; - - # determine speed - my $speed = ($e ? $self->speed : 'travel'); - - # output speed if it's different from last one used - # (goal: reduce gcode size) - my $append_bridge_off = 0; - if ($speed ne $self->last_speed) { - if ($speed eq 'bridge') { - $gcode = ";_BRIDGE_FAN_START\n$gcode"; - } elsif ($self->last_speed eq 'bridge') { - $append_bridge_off = 1; - } - - # 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 * $1/100) - : $Slic3r::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; - $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", $comment if $comment && $Slic3r::gcode_comments; - if ($append_bridge_off) { - $gcode .= "\n;_BRIDGE_FAN_END"; - } - return "$gcode\n"; -} - -sub set_tool { - my $self = shift; - my ($tool) = @_; - - return $self->retract . sprintf "T%d%s\n", $tool, ($Slic3r::gcode_comments ? ' ; change tool' : ''); -} - -sub set_fan { - my $self = shift; - my ($speed, $dont_save) = @_; - - 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' : ''); - } else { - return sprintf "M106 %s%d%s\n", ($Slic3r::gcode_flavor eq 'mach3' ? 'P' : 'S'), - (255 * $speed / 100), ($Slic3r::gcode_comments ? ' ; enable fan' : ''); - } - } - return ""; -} - -sub set_temperature { - my $self = shift; - my ($temperature, $wait) = @_; - - return "" if $wait && $Slic3r::gcode_flavor eq 'makerbot'; - - my ($code, $comment) = $wait - ? ('M109', 'wait for temperature to be reached') - : ('M104', 'set temperature'); - return sprintf "$code %s%d ; $comment\n", - ($Slic3r::gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; -} - -sub set_bed_temperature { - my $self = shift; - my ($temperature, $wait) = @_; - - my ($code, $comment) = $wait - ? (($Slic3r::gcode_flavor eq 'makerbot' ? 'M109' : '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; + return Slic3r::Flow->new(nozzle_diameter => $self->nozzle_diameter, @_); } 1; diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 58284634..d0257cbb 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -147,7 +147,7 @@ sub make_fill { $filler = $Slic3r::solid_fill_pattern; if ($is_bridge) { $filler = 'rectilinear'; - $flow_spacing = sqrt($Slic3r::bridge_flow_ratio * ($Slic3r::nozzle_diameter**2)); + $flow_spacing = sqrt($Slic3r::bridge_flow_ratio * ($layer->infill_flow->nozzle_diameter**2)); } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { $filler = 'rectilinear'; } diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 4fe37bcb..23468254 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -3,48 +3,47 @@ use Moo; use Slic3r::Geometry qw(PI); -has 'width' => (is => 'rw'); -has 'min_spacing' => (is => 'rw'); -has 'spacing' => (is => 'rw'); -has 'layer_height' => (is => 'lazy', builder => 1); +has 'nozzle_diameter' => (is => 'rw', required => 1); +has 'layer_height' => (is => 'rw', default => sub { $Slic3r::layer_height }); -sub _build_layer_height { $Slic3r::layer_height } +has 'width' => (is => 'rw'); +has 'min_spacing' => (is => 'rw'); +has 'spacing' => (is => 'rw'); -sub calculate { +sub BUILD { my $self = shift; - my ($extrusion_width) = @_; my ($flow_width, $min_flow_spacing, $flow_spacing); - if ($extrusion_width) { - $flow_width = $extrusion_width =~ /^(\d+(?:\.\d+)?)%$/ + if ($self->width) { + $flow_width = $self->width =~ /^(\d+(?:\.\d+)?)%$/ ? ($self->layer_height * $1 / 100) - : $extrusion_width; + : $self->width; } else { # here we calculate a sane default by matching the flow speed (at the nozzle) # and the feed rate - my $volume = ($Slic3r::nozzle_diameter**2) * PI/4; - my $shape_threshold = $Slic3r::nozzle_diameter * $self->layer_height + my $volume = ($self->nozzle_diameter**2) * PI/4; + my $shape_threshold = $self->nozzle_diameter * $self->layer_height + ($self->layer_height**2) * PI/4; if ($volume >= $shape_threshold) { # rectangle with semicircles at the ends - $flow_width = (($Slic3r::nozzle_diameter**2) * PI + ($self->layer_height**2) * (4 - PI)) / (4 * $self->layer_height); + $flow_width = (($self->nozzle_diameter**2) * PI + ($self->layer_height**2) * (4 - PI)) / (4 * $self->layer_height); } else { # rectangle with squished semicircles at the ends - $flow_width = $Slic3r::nozzle_diameter * ($Slic3r::nozzle_diameter/$self->layer_height - 4/PI + 1); + $flow_width = $self->nozzle_diameter * ($self->nozzle_diameter/$self->layer_height - 4/PI + 1); } - my $min_flow_width = $Slic3r::nozzle_diameter * 1.05; - my $max_flow_width = $Slic3r::nozzle_diameter * 1.4; + my $min_flow_width = $self->nozzle_diameter * 1.05; + my $max_flow_width = $self->nozzle_diameter * 1.4; $flow_width = $max_flow_width if $flow_width > $max_flow_width; $flow_width = $min_flow_width if $flow_width < $min_flow_width; } - if ($flow_width >= ($Slic3r::nozzle_diameter + $self->layer_height)) { + if ($flow_width >= ($self->nozzle_diameter + $self->layer_height)) { # rectangle with semicircles at the ends $min_flow_spacing = $flow_width - $self->layer_height * (1 - PI/4); } else { # rectangle with shrunk semicircles at the ends - $min_flow_spacing = $flow_width * (1 - PI/4) + $Slic3r::nozzle_diameter * PI/4; + $min_flow_spacing = $flow_width * (1 - PI/4) + $self->nozzle_diameter * PI/4; } $flow_spacing = $flow_width - $Slic3r::overlap_factor * ($flow_width - $min_flow_spacing); diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm new file mode 100644 index 00000000..4e299669 --- /dev/null +++ b/lib/Slic3r/GCode.pm @@ -0,0 +1,420 @@ +package Slic3r::GCode; +use Moo; + +use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Geometry qw(scale unscale); + +has 'layer' => (is => 'rw'); +has 'shift_x' => (is => 'rw', default => sub {0} ); +has 'shift_y' => (is => 'rw', default => sub {0} ); +has 'z' => (is => 'rw', default => sub {0} ); +has 'speed' => (is => 'rw'); + +has 'extruder' => (is => 'rw', default => sub { $Slic3r::extruders->[0] }); +has 'extrusion_distance' => (is => 'rw', default => sub {0} ); +has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds +has 'total_extrusion_length' => (is => 'rw', default => sub {0} ); +has 'retracted' => (is => 'rw', default => sub {1} ); # this spits out some plastic at start +has 'lifted' => (is => 'rw', default => sub {0} ); +has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } ); +has 'last_speed' => (is => 'rw', default => sub {""}); +has 'last_fan_speed' => (is => 'rw', default => sub {0}); +has 'dec' => (is => 'ro', default => sub { 3 } ); + +# calculate speeds (mm/min) +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'), + }}, +); + +my %role_speeds = ( + &EXTR_ROLE_PERIMETER => 'perimeter', + &EXTR_ROLE_SMALLPERIMETER => 'small_perimeter', + &EXTR_ROLE_EXTERNAL_PERIMETER => 'external_perimeter', + &EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER => 'perimeter', + &EXTR_ROLE_FILL => 'infill', + &EXTR_ROLE_SOLIDFILL => 'solid_infill', + &EXTR_ROLE_TOPSOLIDFILL => 'top_solid_infill', + &EXTR_ROLE_BRIDGE => 'bridge', + &EXTR_ROLE_SKIRT => 'perimeter', + &EXTR_ROLE_SUPPORTMATERIAL => 'perimeter', +); + +use Slic3r::Geometry qw(points_coincide PI X Y); + +sub change_layer { + my $self = shift; + my ($layer) = @_; + + $self->layer($layer); + my $z = $Slic3r::z_offset + $layer->print_z * $Slic3r::scaling_factor; + + my $gcode = ""; + + $gcode .= $self->retract(move_z => $z); + $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; + + return $gcode; +} + +sub extrude { + my $self = shift; + + if ($_[0]->isa('Slic3r::ExtrusionLoop')) { + $self->extrude_loop(@_); + } else { + $_[0]->deserialize; + $self->extrude_path(@_); + } +} + +sub extrude_loop { + my $self = shift; + my ($loop, $description) = @_; + + # extrude all loops ccw + $loop->deserialize; + $loop->polygon->make_counter_clockwise; + + # 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) { + 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); + } + my $start_at = $loop->nearest_point_to($last_pos); + + # split the loop at the starting point and make a path + my $extrusion_path = $loop->split_at($start_at); + $extrusion_path->deserialize; + + # clip the path to avoid the extruder to get exactly on the first point of the loop; + # if polyline was shorter than the clipping distance we'd get a null polyline, so + # we discard it in that case + $extrusion_path->clip_end(scale($self->layer ? $self->layer->flow->width : $Slic3r::flow->width) * 0.15); + return '' if !@{$extrusion_path->polyline}; + + # extrude along the path + return $self->extrude_path($extrusion_path, $description); +} + +sub extrude_path { + my $self = shift; + my ($path, $description, $recursive) = @_; + + $path->merge_continuous_lines; + + # detect arcs + if ($Slic3r::gcode_arcs && !$recursive) { + my $gcode = ""; + foreach my $arc_path ($path->detect_arcs) { + $arc_path->deserialize; + $gcode .= $self->extrude_path($arc_path, $description, 1); + } + return $gcode; + } + + my $gcode = ""; + + # 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/; + + if ($distance_from_last_pos >= $distance_threshold) { + $gcode .= $self->retract(travel_to => $path->points->[0]); + } + } + + # go to first point of extrusion path + $gcode .= $self->G0($path->points->[0], undef, 0, "move to first $description point") + if !points_coincide($self->last_pos, $path->points->[0]); + + # compensate retraction + $gcode .= $self->unretract if $self->retracted; + + # calculate extrusion length per distance unit + my $s = $path->flow_spacing || $self->layer ? $self->layer->flow->spacing : $Slic3r::flow->spacing; + my $h = $path->depth_layers * $self->layer->height; + my $w = ($s - ($self->layer ? $self->layer->flow->min_spacing : $Slic3r::flow->min_spacing) * $Slic3r::overlap_factor) / (1 - $Slic3r::overlap_factor); + + my $area; + if ($path->role == EXTR_ROLE_BRIDGE) { + $area = ($s**2) * PI/4; + } elsif ($w >= ($self->extruder->nozzle_diameter + $h)) { + # rectangle with semicircles at the ends + $area = $w * $h + ($h**2) / 4 * (PI - 4); + } else { + # rectangle with shrunk semicircles at the ends + $area = $self->extruder->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4; + } + + my $e = $self->extruder->e_per_mmc * $area; + + # extrude arc or line + $self->speed( $role_speeds{$path->role} || die "Unknown role: " . $path->role ); + my $path_length = 0; + if ($path->isa('Slic3r::ExtrusionPath::Arc')) { + $path_length = $path->length; + $gcode .= $self->G2_G3($path->points->[-1], $path->orientation, + $path->center, $e * $path_length, $description); + } else { + foreach my $line ($path->lines) { + my $line_length = $line->length; + $path_length += $line_length; + $gcode .= $self->G1($line->b, undef, $e * $line_length, $description); + } + } + + if ($Slic3r::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 / ($1/100) + : unscale($path_length) / $Slic3r::first_layer_speed * 60; + } + $self->elapsed_time($self->elapsed_time + $path_time); + } + + return $gcode; +} + +sub retract { + my $self = shift; + my %params = @_; + + return "" unless $Slic3r::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}) + ? undef + : [undef, $self->z + $Slic3r::retract_lift, 0, 'lift plate during retraction']; + + my $gcode = ""; + if (($Slic3r::g0 || $Slic3r::gcode_flavor eq 'mach3') && $params{travel_to}) { + if ($lift) { + # combine lift and retract + $lift->[2] = $retract->[2]; + $gcode .= $self->G0(@$lift); + } else { + # combine travel and 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}) { + # 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']; + $gcode .= $self->G0(@$travel); + $self->lifted(1); + } elsif ($lift) { + $gcode .= $self->G1(@$lift); + } + } + $self->retracted(1); + $self->lifted(1) if $lift; + + # reset extrusion distance during retracts + # this makes sure we leave sufficient precision in the firmware + if (!$Slic3r::use_relative_e_distances && $Slic3r::gcode_flavor !~ /^(?:mach3|makerbot)$/) { + $gcode .= "G92 " . $Slic3r::extrusion_axis . "0\n" if $Slic3r::extrusion_axis; + $self->extrusion_distance(0); + } + + return $gcode; +} + +sub unretract { + my $self = shift; + $self->retracted(0); + my $gcode = ""; + + if ($self->lifted) { + $gcode .= $self->G0(undef, $self->z - $Slic3r::retract_lift, 0, 'restore layer Z'); + $self->lifted(0); + } + + $self->speed('retract'); + $gcode .= $self->G0(undef, undef, ($Slic3r::retract_length + $Slic3r::retract_restart_extra), + "compensate retraction"); + + return $gcode; +} + +sub set_acceleration { + my $self = shift; + my ($acceleration) = @_; + return "" unless $Slic3r::acceleration; + + return sprintf "M201 E%s%s\n", + $acceleration, ($Slic3r::gcode_comments ? ' ; adjust acceleration' : ''); +} + +sub G0 { + my $self = shift; + return $self->G1(@_) if !($Slic3r::g0 || $Slic3r::gcode_flavor eq 'mach3'); + return $self->_G0_G1("G0", @_); +} + +sub G1 { + my $self = shift; + return $self->_G0_G1("G1", @_); +} + +sub _G0_G1 { + my $self = shift; + my ($gcode, $point, $z, $e, $comment) = @_; + my $dec = $self->dec; + + 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; #** + $self->last_pos($point); + } + if (defined $z && $z != $self->z) { + $self->z($z); + $gcode .= sprintf " Z%.${dec}f", $z; + } + + return $self->_Gx($gcode, $e, $comment); +} + +sub G2_G3 { + my $self = shift; + my ($point, $orientation, $center, $e, $comment) = @_; + my $dec = $self->dec; + + 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; #** + + # 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; + + $self->last_pos($point); + return $self->_Gx($gcode, $e, $comment); +} + +sub _Gx { + my $self = shift; + my ($gcode, $e, $comment) = @_; + my $dec = $self->dec; + + # determine speed + my $speed = ($e ? $self->speed : 'travel'); + + # output speed if it's different from last one used + # (goal: reduce gcode size) + my $append_bridge_off = 0; + if ($speed ne $self->last_speed) { + if ($speed eq 'bridge') { + $gcode = ";_BRIDGE_FAN_START\n$gcode"; + } elsif ($self->last_speed eq 'bridge') { + $append_bridge_off = 1; + } + + # 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 * $1/100) + : $Slic3r::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; + $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", $comment if $comment && $Slic3r::gcode_comments; + if ($append_bridge_off) { + $gcode .= "\n;_BRIDGE_FAN_END"; + } + return "$gcode\n"; +} + +sub set_tool { + my $self = shift; + my ($tool) = @_; + + return $self->retract . sprintf "T%d%s\n", $tool, ($Slic3r::gcode_comments ? ' ; change tool' : ''); +} + +sub set_fan { + my $self = shift; + my ($speed, $dont_save) = @_; + + 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' : ''); + } else { + return sprintf "M106 %s%d%s\n", ($Slic3r::gcode_flavor eq 'mach3' ? 'P' : 'S'), + (255 * $speed / 100), ($Slic3r::gcode_comments ? ' ; enable fan' : ''); + } + } + return ""; +} + +sub set_temperature { + my $self = shift; + my ($temperature, $wait) = @_; + + return "" if $wait && $Slic3r::gcode_flavor eq 'makerbot'; + + my ($code, $comment) = $wait + ? ('M109', 'wait for temperature to be reached') + : ('M104', 'set temperature'); + return sprintf "$code %s%d ; $comment\n", + ($Slic3r::gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; +} + +sub set_bed_temperature { + my $self = shift; + my ($temperature, $wait) = @_; + + my ($code, $comment) = $wait + ? (($Slic3r::gcode_flavor eq 'makerbot' ? 'M109' : '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; +} + +1; diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm index e9855ed8..f50d25d9 100644 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ b/lib/Slic3r/GUI/OptionsGroup.pm @@ -26,6 +26,9 @@ sub new { foreach my $opt_key (@{$p{options}}) { + my $index; + $opt_key =~ s/#(\d+)$// and $index = $1; + my $opt = $Slic3r::Config::Options->{$opt_key}; my $label = Wx::StaticText->new($parent, -1, "$opt->{label}:", Wx::wxDefaultPosition, [$p{label_width} || 180, -1]); @@ -44,12 +47,28 @@ sub new { $size = Wx::Size->new($opt->{width} || -1, $opt->{height} || -1); } - my ($get, $set) = $opt->{type} eq 's@' ? qw(serialize deserialize) : qw(get_raw set); + # if it's an array type but no index was specified, use the serialized version + my ($get_m, $set_m) = $opt->{type} =~ /\@$/ && !defined $index + ? qw(serialize deserialize) + : qw(get_raw set); - $field = Wx::TextCtrl->new($parent, -1, Slic3r::Config->$get($opt_key), - Wx::wxDefaultPosition, $size, $style); - EVT_TEXT($parent, $field, sub { Slic3r::Config->$set($opt_key, $field->GetValue) }); - push @reload_callbacks, sub { $field->SetValue(Slic3r::Config->$get($opt_key)) }; + my $get = sub { + my $val = Slic3r::Config->$get_m($opt_key); + $val = $val->[$index] if defined $index; + return $val; + }; + $field = Wx::TextCtrl->new($parent, -1, $get->(), Wx::wxDefaultPosition, $size, $style); + push @reload_callbacks, sub { $field->SetValue($get->()) }; + + my $set = sub { + my $val = $field->GetValue; + if (defined $index) { + Slic3r::Config->$get_m($opt_key)->[$index] = $val; + } else { + Slic3r::Config->$set_m($opt_key, $val); + } + }; + EVT_TEXT($parent, $field, sub { $set->() }); } elsif ($opt->{type} eq 'bool') { $field = Wx::CheckBox->new($parent, -1, ""); $field->SetValue(Slic3r::Config->get_raw($opt_key)); diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index 47470095..77bbfc84 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -21,14 +21,15 @@ sub new { my ($parent) = @_; my $self = $class->SUPER::new($parent, -1); + no warnings 'qw'; my %panels = ( printer => { title => 'Printer', - options => [qw(nozzle_diameter bed_size print_center z_offset gcode_flavor use_relative_e_distances)], + options => [qw(nozzle_diameter#0 bed_size print_center z_offset gcode_flavor use_relative_e_distances)], }, filament => { title => 'Filament', - options => [qw(filament_diameter extrusion_multiplier temperature first_layer_temperature bed_temperature first_layer_bed_temperature)], + options => [qw(filament_diameter#0 extrusion_multiplier#0 temperature first_layer_temperature bed_temperature first_layer_bed_temperature)], }, print_speed => { title => 'Print speed', diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 1ce3aea3..ecf811d8 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -507,21 +507,21 @@ sub write_gcode { print $fh "\n"; # set up our extruder object - my $extruder = Slic3r::Extruder->new; + my $gcodegen = Slic3r::GCode->new; my $min_print_speed = 60 * $Slic3r::min_print_speed; - my $dec = $extruder->dec; + my $dec = $gcodegen->dec; if ($Slic3r::support_material && $Slic3r::support_material_tool > 0) { - print $fh $extruder->set_tool(0); + print $fh $gcodegen->set_tool(0); } - print $fh $extruder->set_fan(0, 1) if $Slic3r::cooling && $Slic3r::disable_fan_first_layers; + print $fh $gcodegen->set_fan(0, 1) if $Slic3r::cooling && $Slic3r::disable_fan_first_layers; # write start commands to file - printf $fh $extruder->set_bed_temperature($Slic3r::first_layer_bed_temperature, 1), + printf $fh $gcodegen->set_bed_temperature($Slic3r::first_layer_bed_temperature, 1), if $Slic3r::first_layer_bed_temperature && $Slic3r::start_gcode !~ /M190/i; - printf $fh $extruder->set_temperature($Slic3r::first_layer_temperature) + printf $fh $gcodegen->set_temperature($Slic3r::first_layer_temperature) if $Slic3r::first_layer_temperature; printf $fh "%s\n", Slic3r::Config->replace_options($Slic3r::start_gcode); - printf $fh $extruder->set_temperature($Slic3r::first_layer_temperature, 1) + printf $fh $gcodegen->set_temperature($Slic3r::first_layer_temperature, 1) if $Slic3r::first_layer_temperature && $Slic3r::start_gcode !~ /M109/i; print $fh "G90 ; use absolute coordinates\n"; print $fh "G21 ; set units to millimeters\n"; @@ -551,31 +551,31 @@ sub write_gcode { my $gcode = ""; if ($layer_id == 1) { - $gcode .= $extruder->set_temperature($Slic3r::temperature) + $gcode .= $gcodegen->set_temperature($Slic3r::temperature) if $Slic3r::temperature && $Slic3r::temperature != $Slic3r::first_layer_temperature; - $gcode .= $extruder->set_bed_temperature($Slic3r::bed_temperature) + $gcode .= $gcodegen->set_bed_temperature($Slic3r::bed_temperature) if $Slic3r::first_layer_bed_temperature && $Slic3r::bed_temperature != $Slic3r::first_layer_bed_temperature; } # go to layer (just use the first one, we only need Z from it) - $gcode .= $extruder->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]); - $extruder->elapsed_time(0); + $gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]); + $gcodegen->elapsed_time(0); # extrude skirt if ($skirt_done < $Slic3r::skirt_height) { - $extruder->shift_x($shift[X]); - $extruder->shift_y($shift[Y]); - $gcode .= $extruder->set_acceleration($Slic3r::perimeter_acceleration); + $gcodegen->shift_x($shift[X]); + $gcodegen->shift_y($shift[Y]); + $gcode .= $gcodegen->set_acceleration($Slic3r::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)) { - $gcode .= $extruder->extrude_loop($_, 'skirt') for @{$self->skirt}; + $gcode .= $gcodegen->extrude_loop($_, 'skirt') for @{$self->skirt}; } $skirt_done++; } # extrude brim if ($layer_id == 0) { - $gcode .= $extruder->extrude_loop($_, 'brim') for @{$self->brim}; + $gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim}; } for my $obj_copy (@$object_copies) { @@ -584,28 +584,28 @@ sub write_gcode { # retract explicitely because changing the shift_[xy] properties below # won't always trigger the automatic retraction - $gcode .= $extruder->retract; + $gcode .= $gcodegen->retract; - $extruder->shift_x($shift[X] + unscale $copy->[X]); - $extruder->shift_y($shift[Y] + unscale $copy->[Y]); + $gcodegen->shift_x($shift[X] + unscale $copy->[X]); + $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]); # extrude perimeters - $gcode .= $extruder->extrude($_, 'perimeter') for @{ $layer->perimeters }; + $gcode .= $gcodegen->extrude($_, 'perimeter') for @{ $layer->perimeters }; # extrude fills - $gcode .= $extruder->set_acceleration($Slic3r::infill_acceleration); + $gcode .= $gcodegen->set_acceleration($Slic3r::infill_acceleration); for my $fill (@{ $layer->fills }) { - $gcode .= $extruder->extrude_path($_, 'fill') - for $fill->shortest_path($extruder->last_pos); + $gcode .= $gcodegen->extrude_path($_, 'fill') + for $fill->shortest_path($gcodegen->last_pos); } # extrude support material if ($layer->support_fills) { - $gcode .= $extruder->set_tool($Slic3r::support_material_tool) + $gcode .= $gcodegen->set_tool($Slic3r::support_material_tool) if $Slic3r::support_material_tool > 0; - $gcode .= $extruder->extrude_path($_, 'support material') - for $layer->support_fills->shortest_path($extruder->last_pos); - $gcode .= $extruder->set_tool(0) + $gcode .= $gcodegen->extrude_path($_, 'support material') + for $layer->support_fills->shortest_path($gcodegen->last_pos); + $gcode .= $gcodegen->set_tool(0) if $Slic3r::support_material_tool > 0; } } @@ -614,7 +614,7 @@ sub write_gcode { my $fan_speed = $Slic3r::fan_always_on ? $Slic3r::min_fan_speed : 0; my $speed_factor = 1; if ($Slic3r::cooling) { - my $layer_time = $extruder->elapsed_time; + 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; @@ -634,14 +634,14 @@ sub write_gcode { } $fan_speed = 0 if $layer_id < $Slic3r::disable_fan_first_layers; } - $gcode = $extruder->set_fan($fan_speed) . $gcode; + $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) { $gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm; } else { - $gcode =~ s/^;_BRIDGE_FAN_START\n/ $extruder->set_fan($Slic3r::bridge_fan_speed, 1) /gmex; - $gcode =~ s/^;_BRIDGE_FAN_END\n/ $extruder->set_fan($fan_speed, 1) /gmex; + $gcode =~ s/^;_BRIDGE_FAN_START\n/ $gcodegen->set_fan($Slic3r::bridge_fan_speed, 1) /gmex; + $gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex; } return $gcode; @@ -661,10 +661,10 @@ sub write_gcode { # this happens before Z goes down to layer 0 again, so that # no collision happens hopefully. if ($finished_objects > 0) { - $extruder->shift_x($shift[X] + unscale $copy->[X]); - $extruder->shift_y($shift[Y] + unscale $copy->[Y]); - print $fh $extruder->retract; - print $fh $extruder->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object'); + $gcodegen->shift_x($shift[X] + unscale $copy->[X]); + $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]); + print $fh $gcodegen->retract; + print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object'); } for my $layer_id (0..$#{$self->objects->[$obj_idx]->layers}) { @@ -672,9 +672,9 @@ 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 $extruder->set_bed_temperature($Slic3r::first_layer_bed_temperature), + printf $fh $gcodegen->set_bed_temperature($Slic3r::first_layer_bed_temperature), if $Slic3r::first_layer_bed_temperature; - printf $fh $extruder->set_temperature($Slic3r::first_layer_temperature) + printf $fh $gcodegen->set_temperature($Slic3r::first_layer_temperature) if $Slic3r::first_layer_temperature; } print $fh $extrude_layer->($layer_id, [[ $obj_idx, $copy ]]); @@ -693,11 +693,11 @@ sub write_gcode { } # save statistic data - $self->total_extrusion_length($extruder->total_extrusion_length); + $self->total_extrusion_length($gcodegen->total_extrusion_length); # write end commands to file - print $fh $extruder->retract; - print $fh $extruder->set_fan(0); + 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); @@ -710,7 +710,7 @@ sub write_gcode { sub total_extrusion_volume { my $self = shift; - return $self->total_extrusion_length * ($Slic3r::filament_diameter**2) * PI/4 / 1000; + return $self->total_extrusion_length * ($Slic3r::extruders->[0]->filament_diameter**2) * PI/4 / 1000; } # this method will return the value of $self->output_file after expanding its